From 3a30b3d11d616b8129167d19c6799c3387cf04e3 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 22 Mar 2025 18:18:39 +0200 Subject: [PATCH 001/162] Updating the docs to reflect the actual implementation (#10276) --- broadcasting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 88869cd41b..0cde2837e2 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -186,8 +186,8 @@ 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, + wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, + wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', enabledTransports: ['ws', 'wss'], }); From b489ae51a728eee1138f3e3bb8d2cafe8d04252b Mon Sep 17 00:00:00 2001 From: Jonas Boserup Date: Sat, 22 Mar 2025 17:53:52 +0100 Subject: [PATCH 002/162] [12.x] Add schedule useCache example (#10273) * Add schedule useCache example * formatting --------- Co-authored-by: Taylor Otwell --- scheduling.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scheduling.md b/scheduling.md index a335055b62..d938d24cd0 100644 --- a/scheduling.md +++ b/scheduling.md @@ -362,6 +362,15 @@ Schedule::command('report:generate') ->onOneServer(); ``` +You may use the `useCache` method to customize the cache store used by the scheduler to obtain the atomic locks necessary for single-server tasks: + +```php +Schedule::command('recipes:sync') + ->everyThirtyMinutes() + ->onOneServer(); + ->useCache('database'); +``` + #### Naming Single Server Jobs From a7e90ceda8525dc5ea079d4450d30c6345ac332d Mon Sep 17 00:00:00 2001 From: Jesper Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Sat, 22 Mar 2025 18:14:15 +0100 Subject: [PATCH 003/162] [12.x] Add `DatabaseTokenRepository` constructor signature update to upgrade guide (#10271) * Add DatabaseTokenRepository constructor signature update to 12.x upgrade guide * formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/upgrade.md b/upgrade.md index 0e609d947a..e4e38079ad 100644 --- a/upgrade.md +++ b/upgrade.md @@ -91,6 +91,16 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage 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. + +### Authentication + + +#### Updated `DatabaseTokenRepository` Constructor Signature + +**Likelihood Of Impact: Very Low** + +The constructor of the `Illuminate\Auth\Passwords\DatabaseTokenRepository` class now expects the `$expires` parameter to be given in seconds, rather than minutes. + ### Concurrency From 379d9a55ae715c67c561565f62b87636c2371c54 Mon Sep 17 00:00:00 2001 From: Tech Wolf Date: Mon, 24 Mar 2025 20:12:44 +0530 Subject: [PATCH 004/162] [12.x] Cache Event List with Cache Flush and Missed Events (#10278) --- cache.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/cache.md b/cache.md index b5a9d2a455..dd8c522426 100644 --- a/cache.md +++ b/cache.md @@ -530,12 +530,21 @@ To execute code on every cache operation, you may listen for various [events](/d
-| Event Name | -| --- | -| `Illuminate\Cache\Events\CacheHit` | -| `Illuminate\Cache\Events\CacheMissed` | -| `Illuminate\Cache\Events\KeyForgotten` | -| `Illuminate\Cache\Events\KeyWritten` | +| Event Name | +|----------------------------------------------| +| `Illuminate\Cache\Events\CacheFlushed` | +| `Illuminate\Cache\Events\CacheFlushing` | +| `Illuminate\Cache\Events\CacheHit` | +| `Illuminate\Cache\Events\CacheMissed` | +| `Illuminate\Cache\Events\ForgettingKey` | +| `Illuminate\Cache\Events\KeyForgetFailed` | +| `Illuminate\Cache\Events\KeyForgotten` | +| `Illuminate\Cache\Events\KeyWriteFailed` | +| `Illuminate\Cache\Events\KeyWritten` | +| `Illuminate\Cache\Events\RetrievingKey` | +| `Illuminate\Cache\Events\RetrievingManyKeys` | +| `Illuminate\Cache\Events\WritingKey` | +| `Illuminate\Cache\Events\WritingManyKeys` |
From 53a68fe6d91ae55877c0ee47ecfe4ddb237ed553 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 28 Mar 2025 04:16:30 +1100 Subject: [PATCH 005/162] Improve `Collection::reduce` docs (#10282) * Document that keys are always passed, regardless of array type. * Fix incorrect and broken type * Give initial value to satisfy `int` type in strict setting --- collections.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/collections.md b/collections.md index 5bf01293e5..1faa855691 100644 --- a/collections.md +++ b/collections.md @@ -2402,7 +2402,7 @@ $collection->reduce(function (int $carry, int $item) { // 10 ``` -The `reduce` method also passes array keys in associative collections to the given callback: +The `reduce` method also passes array keys to the given callback: ```php $collection = collect([ @@ -2417,9 +2417,9 @@ $ratio = [ 'eur' => 1.22, ]; -$collection->reduce(function (int $carry, int $value, int $key) use ($ratio) { +$collection->reduce(function (int $carry, int $value, string $key) use ($ratio) { return $carry + ($value * $ratio[$key]); -}); +}, 0); // 4264 ``` From cdcd3ca73fbcb95b4d8d522863a531b9b95ef1eb Mon Sep 17 00:00:00 2001 From: Stanislav Date: Thu, 27 Mar 2025 18:35:57 +0100 Subject: [PATCH 006/162] Fix docs - pessimistic locking SQL (#10280) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 2637921311..70b0a0ec5a 100644 --- a/queries.md +++ b/queries.md @@ -1447,7 +1447,7 @@ DB::transaction(function () { ->find(1); $receiver = DB::table('users') - ->lockForUpdate(); + ->lockForUpdate() ->find(2); if ($sender->balance < 100) { From 9a51f364f285a6c32cdfe5b5450deffd194d5717 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 28 Mar 2025 16:05:20 +0000 Subject: [PATCH 007/162] docs: parameter is `$escape`, not `$escaped` (#10287) --- http-tests.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/http-tests.md b/http-tests.md index acd49b83ad..a5cf2c4f39 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1108,7 +1108,7 @@ $response->assertCreated(); 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`: ```php -$response->assertDontSee($value, $escaped = true); +$response->assertDontSee($value, $escape = true); ``` @@ -1117,7 +1117,7 @@ $response->assertDontSee($value, $escaped = true); 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: ```php -$response->assertDontSeeText($value, $escaped = true); +$response->assertDontSeeText($value, $escape = true); ``` @@ -1577,7 +1577,7 @@ $response->assertRequestTimeout(); 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`: ```php -$response->assertSee($value, $escaped = true); +$response->assertSee($value, $escape = true); ``` @@ -1586,7 +1586,7 @@ $response->assertSee($value, $escaped = true); 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`: ```php -$response->assertSeeInOrder(array $values, $escaped = true); +$response->assertSeeInOrder(array $values, $escape = true); ``` @@ -1595,7 +1595,7 @@ $response->assertSeeInOrder(array $values, $escaped = true); 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: ```php -$response->assertSeeText($value, $escaped = true); +$response->assertSeeText($value, $escape = true); ``` @@ -1604,7 +1604,7 @@ $response->assertSeeText($value, $escaped = true); 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: ```php -$response->assertSeeTextInOrder(array $values, $escaped = true); +$response->assertSeeTextInOrder(array $values, $escape = true); ``` From 02192ada3cb8e85f4b2b08011c9c7de2b1d254fa Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 29 Mar 2025 18:55:01 +0200 Subject: [PATCH 008/162] Missing semi-colon (#10288) --- broadcasting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 0cde2837e2..ec87e0a207 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -901,7 +901,7 @@ If you would like to stop listening to a given event without [leaving the channe ```js Echo.private(`orders.${this.order.id}`) - .stopListening('OrderShipmentStatusUpdated') + .stopListening('OrderShipmentStatusUpdated'); ``` @@ -1145,7 +1145,7 @@ 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 an `App\Models\User` model with an `id` of `1`: ```php -$user->broadcastChannel() +$user->broadcastChannel(); ``` From 7e4adca0bae736fb0f505ade05faf5ff13d1c973 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 29 Mar 2025 12:32:44 -0500 Subject: [PATCH 009/162] arr sole --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index ae1d78e1ac..83d113dbe5 100644 --- a/helpers.md +++ b/helpers.md @@ -71,6 +71,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::select](#method-array-select) [Arr::set](#method-array-set) [Arr::shuffle](#method-array-shuffle) +[Arr::sole](#method-array-sole) [Arr::sort](#method-array-sort) [Arr::sortDesc](#method-array-sort-desc) [Arr::sortRecursive](#method-array-sort-recursive) @@ -920,6 +921,21 @@ $array = Arr::shuffle([1, 2, 3, 4, 5]); // [3, 2, 5, 1, 4] - (generated randomly) ``` + +#### `Arr::sole()` {.collection-method} + +The `Arr::sole` method retrieves a single value from an array using the given closure. If more than one value within the array matches the given truth test, an `Illuminate\Support\MultipleItemsFoundException` exception will be thrown. If no values match the truth test, an `Illuminate\Support\ItemNotFoundException` exception will be thrown: + +```php +use Illuminate\Support\Arr; + +$array = ['Desk', 'Table', 'Chair']; + +$value = Arr::sole($array, fn (string $value) => $value === 'Desk'); + +// 'Desk' +``` + #### `Arr::sort()` {.collection-method} From 3c910b6734d571bdbf050921c1f14f4522079e1d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 29 Mar 2025 12:34:29 -0500 Subject: [PATCH 010/162] as conditions --- eloquent-relationships.md | 8 +++++++- eloquent.md | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index ef753069dc..a75adf48ad 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -725,7 +725,7 @@ public function featuredPosts(): HasMany } ``` -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: +The `withAttributes` method will add `where` conditions to the query using the given attributes, and it will also add the given attributes to any models created via the relationship method: ```php $post = $user->featuredPosts()->create(['title' => 'Featured Post']); @@ -733,6 +733,12 @@ $post = $user->featuredPosts()->create(['title' => 'Featured Post']); $post->featured; // true ``` +To instruct the `withAttributes` method to not add `where` conditions to the query, you may set the `asConditions` argument to `false`: + +```php +return $this->posts()->withAttributes(['featured' => true], asConditions: false); +``` + ## Many to Many Relationships diff --git a/eloquent.md b/eloquent.md index 1c88ce2d18..c7cf42ea8d 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1594,7 +1594,7 @@ class Post extends Model } ``` -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: +The `withAttributes` method will add `where` conditions to the query using the given attributes, and it will also add the given attributes to any models created via the scope: ```php $draft = Post::draft()->create(['title' => 'In Progress']); @@ -1602,6 +1602,14 @@ $draft = Post::draft()->create(['title' => 'In Progress']); $draft->hidden; // true ``` +To instruct the `withAttributes` method to not add `where` conditions to the query, you may set the `asConditions` argument to `false`: + +```php +$query->withAttributes([ + 'hidden' => true, +], asConditions: false); +``` + ## Comparing Models From 5e9ae53c6b46b6f844224578a7ac434badd8382c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 29 Mar 2025 12:37:32 -0500 Subject: [PATCH 011/162] assert doesnt throw --- http-tests.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http-tests.md b/http-tests.md index a5cf2c4f39..70bffc4a2c 100644 --- a/http-tests.md +++ b/http-tests.md @@ -441,6 +441,12 @@ $this->assertThrows( ); ``` +The `assertDoesntThrow` method may be used to assert that the code within a given closure does not throw any exceptions: + +```php +$this->assertDoesntThrow(fn () => (new ProcessOrder)->execute()); +``` + ## Testing JSON APIs From 4c0e15c043963ef44bcabdb0cbbdd8f1e695f7b8 Mon Sep 17 00:00:00 2001 From: Kevin Inman <47095624+inmanturbo@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:42:53 -0400 Subject: [PATCH 012/162] Document the new `Migration::shouldRun()` feature (#10277) * Update migrations.md * Update migrations.md * wording * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/migrations.md b/migrations.md index 34499bb1c2..2d40e5cecd 100644 --- a/migrations.md +++ b/migrations.md @@ -134,6 +134,21 @@ public function up(): void } ``` + +#### Skipping Migrations + +Sometimes a migration might be meant to support a feature that is not yet active and you do not want it to run yet. In this case you may define a `shouldRun` method on the migration. If the `shouldRun` method returns `false`, the migration will be skipped: + +```php +/** + * Determine if this migration should run. + */ +public function shouldRun(): bool +{ + return Feature::active(Flights::class); +} +``` + ## Running Migrations From fe846c64bc236ed39fbcd7da705602add79132f4 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:26:07 +0200 Subject: [PATCH 013/162] Add missing imports in migration example (#10291) --- migrations.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migrations.md b/migrations.md index 2d40e5cecd..a4c9d5cb4f 100644 --- a/migrations.md +++ b/migrations.md @@ -140,6 +140,9 @@ public function up(): void Sometimes a migration might be meant to support a feature that is not yet active and you do not want it to run yet. In this case you may define a `shouldRun` method on the migration. If the `shouldRun` method returns `false`, the migration will be skipped: ```php +use App\Models\Flights; +use Laravel\Pennant\Feature; + /** * Determine if this migration should run. */ From 0a322f96f51c2c154d437ab02127637cbba72d7a Mon Sep 17 00:00:00 2001 From: Jason McCreary Date: Sun, 30 Mar 2025 12:26:26 -0400 Subject: [PATCH 014/162] Remove unnecessary `once` (#10290) --- mocking.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/mocking.md b/mocking.md index 7b707a649b..05b0a9b58c 100644 --- a/mocking.md +++ b/mocking.md @@ -27,7 +27,7 @@ test('something can be mocked', function () { $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); + $mock->expects('process'); }) ); }); @@ -43,7 +43,7 @@ public function test_something_can_be_mocked(): void $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); + $mock->expects('process'); }) ); } @@ -56,7 +56,7 @@ use App\Service; use Mockery\MockInterface; $mock = $this->mock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); + $mock->expects('process'); }); ``` @@ -67,7 +67,7 @@ use App\Service; use Mockery\MockInterface; $mock = $this->partialMock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); + $mock->expects('process'); }); ``` @@ -119,8 +119,7 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, use Illuminate\Support\Facades\Cache; test('get index', function () { - Cache::shouldReceive('get') - ->once() + Cache::expects('get') ->with('key') ->andReturn('value'); @@ -142,8 +141,7 @@ class UserControllerTest extends TestCase { public function test_get_index(): void { - Cache::shouldReceive('get') - ->once() + Cache::expects('get') ->with('key') ->andReturn('value'); @@ -174,7 +172,7 @@ test('values are be stored in cache', function () { $response->assertStatus(200); - Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); + Cache::shouldHaveReceived('put')->with('name', 'Taylor', 10); }); ``` @@ -189,7 +187,7 @@ public function test_values_are_be_stored_in_cache(): void $response->assertStatus(200); - Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); + Cache::shouldHaveReceived('put')->with('name', 'Taylor', 10); } ``` From 4fa8bb88a03baf9ae348cae23d94f27c84fac2d5 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:06:34 +0200 Subject: [PATCH 015/162] Ensure consistent formatting (#10293) --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index dd8c522426..feed3275b6 100644 --- a/cache.md +++ b/cache.md @@ -27,7 +27,7 @@ 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 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. +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. 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. From ac72220c4eafabc7b815005c135a54d13395f918 Mon Sep 17 00:00:00 2001 From: Jason McCreary Date: Mon, 31 Mar 2025 10:07:01 -0400 Subject: [PATCH 016/162] Correct remaining reference (#10294) --- mocking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocking.md b/mocking.md index 05b0a9b58c..55aeca9459 100644 --- a/mocking.md +++ b/mocking.md @@ -111,7 +111,7 @@ class UserController extends Controller } ``` -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: +We can mock the call to the `Cache` facade by using the `expects` 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: ```php tab=Pest Date: Mon, 31 Mar 2025 19:38:19 +0530 Subject: [PATCH 017/162] Remove Laravel Website, no github repo found (#10292) --- contributions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/contributions.md b/contributions.md index 9aa1ac757a..5ff3c116fb 100644 --- a/contributions.md +++ b/contributions.md @@ -51,7 +51,6 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [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) From 1de335bde987981d0d063d48244512cb08772096 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 31 Mar 2025 16:33:01 -0500 Subject: [PATCH 018/162] document new scopes --- eloquent.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/eloquent.md b/eloquent.md index c7cf42ea8d..62ddc73f3c 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1479,7 +1479,7 @@ User::withoutGlobalScopes([ ### Local Scopes -Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with `scope`. +Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, add the `Scope` attribute to an Eloquent method. Scopes should always return the same query builder instance or `void`: @@ -1488,6 +1488,7 @@ Scopes should always return the same query builder instance or `void`: namespace App\Models; +use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -1496,7 +1497,8 @@ class User extends Model /** * Scope a query to only include popular users. */ - public function scopePopular(Builder $query): void + #[Scope] + protected function popular(Builder $query): void { $query->where('votes', '>', 100); } @@ -1504,7 +1506,8 @@ class User extends Model /** * Scope a query to only include active users. */ - public function scopeActive(Builder $query): void + #[Scope] + protected function active(Builder $query): void { $query->where('active', 1); } @@ -1514,7 +1517,7 @@ class User extends Model #### 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: +Once the scope has been defined, you may call the scope methods when querying the model. You can even chain calls to various scopes: ```php use App\Models\User; @@ -1546,6 +1549,7 @@ Sometimes you may wish to define a scope that accepts parameters. To get started namespace App\Models; +use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -1554,7 +1558,8 @@ class User extends Model /** * Scope a query to only include users of a given type. */ - public function scopeOfType(Builder $query, string $type): void + #[Scope] + protected function ofType(Builder $query, string $type): void { $query->where('type', $type); } @@ -1577,6 +1582,7 @@ If you would like to use scopes to create models that have the same attributes a namespace App\Models; +use Illuminate\Database\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -1585,7 +1591,8 @@ class Post extends Model /** * Scope the query to only include drafts. */ - public function scopeDraft(Builder $query): void + #[Scope] + protected function draft(Builder $query): void { $query->withAttributes([ 'hidden' => true, From d8ade08a9ca88019281a5d2e0a3c3c688b0e1d48 Mon Sep 17 00:00:00 2001 From: Igor Kunda <5486970+restuff@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:23:11 +0300 Subject: [PATCH 019/162] Add \Illuminate\Database\Schema\Blueprint changed constructor signature description (#10296) * Add \Illuminate\Database\Schema\Blueprint changed constructor signature description * 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 e4e38079ad..2c3ddb3e30 100644 --- a/upgrade.md +++ b/upgrade.md @@ -181,6 +181,13 @@ $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. + +#### Updated `Blueprint` Constructor Signature + +**Likelihood Of Impact: Very Low** + +The constructor of the `Illuminate\Database\Schema\Blueprint` class now expects an instance of `Illuminate\Database\Connection` as its first argument. + ### Eloquent From 038d72df46b8f2983ab5481bfd7cac69c32249bc Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:08:21 +0200 Subject: [PATCH 020/162] Fix invalid JSON syntax (#10298) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index ec87e0a207..64c2778092 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -1163,7 +1163,7 @@ So, for example, an update to the `App\Models\Post` model would broadcast an eve ... }, ... - "socket": "someSocketId", + "socket": "someSocketId" } ``` From fd18bf04e6c0f4452f769ba99fd215c48e72cec8 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:15:40 +0200 Subject: [PATCH 021/162] [12.x] Add link to Laravel Tinker GitHub repository (#10299) * Add link to Laravel Tinker GitHub repository * 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 879a66e303..52efee9645 100644 --- a/artisan.md +++ b/artisan.md @@ -51,7 +51,7 @@ If you are using [Laravel Sail](/docs/{{version}}/sail) as your local developmen ### Tinker (REPL) -Laravel Tinker is a powerful REPL for the Laravel framework, powered by the [PsySH](https://github.com/bobthecow/psysh) package. +[Laravel Tinker](https://github.com/laravel/tinker) is a powerful REPL for the Laravel framework, powered by the [PsySH](https://github.com/bobthecow/psysh) package. #### Installation From 902aaf97857b1c02499fe0dda7114ce036eb5db8 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:40:48 +0200 Subject: [PATCH 022/162] Update Symfony documentation link to the latest maintained version (#10300) --- contracts.md | 2 +- mail.md | 2 +- processes.md | 2 +- prompts.md | 2 +- requests.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts.md b/contracts.md index e157677ad5..38409b11b9 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/7.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/current/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 eb7c9c5591..4958ddc3c3 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, Resend, 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/current/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 diff --git a/processes.md b/processes.md index a6565b79a5..8e0374750f 100644 --- a/processes.md +++ b/processes.md @@ -23,7 +23,7 @@ ## Introduction -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. +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 diff --git a/prompts.md b/prompts.md index b3f5e48ac6..e0ef04fa0b 100644 --- a/prompts.md +++ b/prompts.md @@ -946,7 +946,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/7.0/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/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. diff --git a/requests.md b/requests.md index 27d70db671..9611fedd8c 100644 --- a/requests.md +++ b/requests.md @@ -787,7 +787,7 @@ In addition to configuring the trusted proxies, you may also configure the proxy ``` > [!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). +> 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/current/deployment/proxies.html). #### Trusting All Proxies From c22c31160494a3eb3f092e86a6be6125a009f982 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Apr 2025 08:56:04 -0500 Subject: [PATCH 023/162] update docs --- telescope.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telescope.md b/telescope.md index de9d418ca4..fe144d0729 100644 --- a/telescope.md +++ b/telescope.md @@ -474,7 +474,7 @@ public function register(): void { // ... - Telescope::avatar(function (string $id, string $email) { + Telescope::avatar(function (?string $id, ?string $email) { return '/avatars/'.User::find($id)->avatar_path; }); } From 7d1df16ae866f18f4f1422750dbeccf59f46a0d4 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:14:28 +0200 Subject: [PATCH 024/162] [12.x] Nullsafe operator (#10304) * Nullsafe operator * Update telescope.md * Update telescope.md --------- Co-authored-by: Taylor Otwell --- telescope.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telescope.md b/telescope.md index fe144d0729..fab3ec9ab4 100644 --- a/telescope.md +++ b/telescope.md @@ -475,7 +475,9 @@ public function register(): void // ... Telescope::avatar(function (?string $id, ?string $email) { - return '/avatars/'.User::find($id)->avatar_path; + return ! is_null($id) + ? '/avatars/'.User::find($id)->avatar_path + : '/generic-avatar.jpg'; }); } ``` From ffb08b0a9997823ef7b53797edf7e4a929af9c5a Mon Sep 17 00:00:00 2001 From: Alexandre Demelas <7119058+axdemelas@users.noreply.github.com> Date: Thu, 3 Apr 2025 22:32:38 -0400 Subject: [PATCH 025/162] change "directory" to "file" in broadcasting configuration description (#10305) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 64c2778092..33460337fa 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -68,7 +68,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. Don't worry if this directory does not exist in your application; it will be created when you run the `install:broadcasting` Artisan command. +All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Don't worry if this file 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. From c4f646c73d91617fee1b4a55c422343df2172e53 Mon Sep 17 00:00:00 2001 From: Alain Cis Date: Fri, 4 Apr 2025 14:57:50 +0100 Subject: [PATCH 026/162] Fix : Formatting markdown in blade.md (#10307) * Fix : Formatting markdown in blade.md * Correct the formatting ref PR #10307 --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 30389aed49..e599de1b96 100644 --- a/blade.md +++ b/blade.md @@ -155,7 +155,7 @@ The `@` symbol may also be used to escape Blade directives: Sometimes you may pass an array to your view with the intention of rendering it as JSON in order to initialize a JavaScript variable. For example: -```blade +```php From e22c77d70605482e9df9964e6baad8839f824292 Mon Sep 17 00:00:00 2001 From: Gilbert Paquin Date: Fri, 4 Apr 2025 10:33:52 -0400 Subject: [PATCH 027/162] [12.x] Add support for Redis Unix socket connections with PhpRedis (#10306) * Update redis, add Unix socket instructions Add Unix socket configuration instructions for Redis * Update redis.md --------- Co-authored-by: Taylor Otwell --- redis.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/redis.md b/redis.md index 9815700778..1991d66767 100644 --- a/redis.md +++ b/redis.md @@ -210,6 +210,16 @@ In addition to the default configuration options, PhpRedis supports the followin ], ``` + +#### Unix Socket Connections + +Redis connections can also be configured to use Unix sockets instead of TCP. This can offer improved performance by eliminating TCP overhead for connections to Redis instances on the same server as your application. To configure Redis to use a Unix socket, set your `REDIS_HOST` environment variable to the path of the Redis socket and the `REDIS_PORT` environment variable to `0`: + +```env +REDIS_HOST=/run/redis/redis.sock +REDIS_PORT=0 +``` + #### PhpRedis Serialization and Compression From 1660ca3b75c6ecc7a88c0439b10717072bd68ec9 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:28:15 +0200 Subject: [PATCH 028/162] Fix link formatting (#10309) --- collections.md | 82 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/collections.md b/collections.md index 1faa855691..e318e3401c 100644 --- a/collections.md +++ b/collections.md @@ -318,7 +318,7 @@ collect([1, 2, 3])->all(); #### `average()` {.collection-method} -Alias for the [`avg`](#method-avg) method. +Alias for the [avg](#method-avg) method. #### `avg()` {.collection-method} @@ -343,7 +343,7 @@ $average = collect([1, 1, 2, 4])->avg(); #### `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: +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: ```php $collection = collect([1, 2, 3, 4, 5]); @@ -565,7 +565,7 @@ $collection->contains('product', 'Bookcase'); // false ``` -The `contains` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`containsStrict`](#method-containsstrict) method to filter using "strict" comparisons. +The `contains` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [containsStrict](#method-containsstrict) method to filter using "strict" comparisons. For the inverse of `contains`, see the [doesntContain](#method-doesntcontain) method. @@ -591,7 +591,7 @@ collect(['1', '2'])->containsOneItem(); #### `containsStrict()` {.collection-method} -This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [contains](#method-contains) method; however, all values are compared using "strict" comparisons. > [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). @@ -699,7 +699,7 @@ $collection->dd(); */ ``` -If you do not want to stop executing the script, use the [`dump`](#method-dump) method instead. +If you do not want to stop executing the script, use the [dump](#method-dump) method instead. #### `diff()` {.collection-method} @@ -766,7 +766,7 @@ $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. +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} @@ -873,7 +873,7 @@ $collection->dump(); */ ``` -If you want to stop executing the script after dumping the collection, use the [`dd`](#method-dd) method instead. +If you want to stop executing the script after dumping the collection, use the [dd](#method-dd) method instead. #### `duplicates()` {.collection-method} @@ -905,7 +905,7 @@ $employees->duplicates('position'); #### `duplicatesStrict()` {.collection-method} -This method has the same signature as the [`duplicates`](#method-duplicates) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [duplicates](#method-duplicates) method; however, all values are compared using "strict" comparisons. #### `each()` {.collection-method} @@ -1661,7 +1661,7 @@ collect([1, 2, 3, 4])->last(); #### `lazy()` {.collection-method} -The `lazy` method returns a new [`LazyCollection`](#lazy-collections) instance from the underlying array of items: +The `lazy` method returns a new [LazyCollection](#lazy-collections) instance from the underlying array of items: ```php $lazyCollection = collect([1, 2, 3, 4])->lazy(); @@ -1715,7 +1715,7 @@ $multiplied->all(); ``` > [!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. +> 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} @@ -2460,7 +2460,7 @@ $filtered->all(); // [1, 2] ``` -For the inverse of the `reject` method, see the [`filter`](#method-filter) method. +For the inverse of the `reject` method, see the [filter](#method-filter) method. #### `replace()` {.collection-method} @@ -2716,7 +2716,7 @@ $slice->all(); // [5, 6] ``` -The returned slice will preserve keys by default. If you do not wish to preserve the original keys, you can use the [`values`](#method-values) method to reindex them. +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} @@ -2733,7 +2733,7 @@ $chunks->toArray(); // [[1, 2], [2, 3], [3, 4], [4, 5]] ``` -This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: +This is especially useful in conjunction with the [eachSpread](#method-eachspread) method: ```php $transactions->sliding(2)->eachSpread(function (Collection $previous, Collection $current) { @@ -2796,12 +2796,12 @@ If there are no elements in the collection that should be returned by the `sole` #### `some()` {.collection-method} -Alias for the [`contains`](#method-contains) method. +Alias for the [contains](#method-contains) method. #### `sort()` {.collection-method} -The `sort` method sorts the collection. The sorted collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `sort` method sorts the collection. The sorted collection keeps the original array keys, so in the following example we will use the [values](#method-values) method to reset the keys to consecutively numbered indexes: ```php $collection = collect([5, 3, 1, 2, 4]); @@ -2813,15 +2813,15 @@ $sorted->values()->all(); // [1, 2, 3, 4, 5] ``` -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. +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] -> If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. +> 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} -The `sortBy` method sorts the collection by the given key. The sorted collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `sortBy` method sorts the collection by the given key. The sorted collection keeps the original array keys, so in the following example we will use the [values](#method-values) method to reset the keys to consecutively numbered indexes: ```php $collection = collect([ @@ -2946,12 +2946,12 @@ $sorted->values()->all(); #### `sortByDesc()` {.collection-method} -This method has the same signature as the [`sortBy`](#method-sortby) method, but will sort the collection in the opposite order. +This method has the same signature as the [sortBy](#method-sortby) method, but will sort the collection in the opposite order. #### `sortDesc()` {.collection-method} -This method will sort the collection in the opposite order as the [`sort`](#method-sort) method: +This method will sort the collection in the opposite order as the [sort](#method-sort) method: ```php $collection = collect([5, 3, 1, 2, 4]); @@ -2963,7 +2963,7 @@ $sorted->values()->all(); // [5, 4, 3, 2, 1] ``` -Unlike `sort`, you may not pass a closure to `sortDesc`. Instead, you should use the [`sort`](#method-sort) method and invert your comparison. +Unlike `sort`, you may not pass a closure to `sortDesc`. Instead, you should use the [sort](#method-sort) method and invert your comparison. #### `sortKeys()` {.collection-method} @@ -2993,7 +2993,7 @@ $sorted->all(); #### `sortKeysDesc()` {.collection-method} -This method has the same signature as the [`sortKeys`](#method-sortkeys) method, but will sort the collection in the opposite order. +This method has the same signature as the [sortKeys](#method-sortkeys) method, but will sort the collection in the opposite order. #### `sortKeysUsing()` {.collection-method} @@ -3020,7 +3020,7 @@ $sorted->all(); */ ``` -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 [`uksort`](https://www.php.net/manual/en/function.uksort.php#refsect1-function.uksort-parameters), which is the PHP function that `sortKeysUsing` method utilizes internally. +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 [uksort](https://www.php.net/manual/en/function.uksort.php#refsect1-function.uksort-parameters), which is the PHP function that `sortKeysUsing` method utilizes internally. #### `splice()` {.collection-method} @@ -3271,7 +3271,7 @@ $collection->toArray(); ``` > [!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. +> `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} @@ -3304,7 +3304,7 @@ $collection->all(); ``` > [!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. +> 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} @@ -3361,7 +3361,7 @@ $union->all(); #### `unique()` {.collection-method} -The `unique` method returns all of the unique items in the collection. The returned collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `unique` method returns all of the unique items in the collection. The returned collection keeps the original array keys, so in the following example we will use the [values](#method-values) method to reset the keys to consecutively numbered indexes: ```php $collection = collect([1, 1, 2, 2, 3, 4, 2]); @@ -3415,7 +3415,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. +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] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). @@ -3423,7 +3423,7 @@ The `unique` method uses "loose" comparisons when checking item values, meaning #### `uniqueStrict()` {.collection-method} -This method has the same signature as the [`unique`](#method-unique) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [unique](#method-unique) method; however, all values are compared using "strict" comparisons. #### `unless()` {.collection-method} @@ -3462,17 +3462,17 @@ $collection->all(); // [1, 2, 3, 5] ``` -For the inverse of `unless`, see the [`when`](#method-when) method. +For the inverse of `unless`, see the [when](#method-when) method. #### `unlessEmpty()` {.collection-method} -Alias for the [`whenNotEmpty`](#method-whennotempty) method. +Alias for the [whenNotEmpty](#method-whennotempty) method. #### `unlessNotEmpty()` {.collection-method} -Alias for the [`whenEmpty`](#method-whenempty) method. +Alias for the [whenEmpty](#method-whenempty) method. #### `unwrap()` {.collection-method} @@ -3569,7 +3569,7 @@ $collection->all(); // [1, 2, 3, 5] ``` -For the inverse of `when`, see the [`unless`](#method-unless) method. +For the inverse of `when`, see the [unless](#method-unless) method. #### `whenEmpty()` {.collection-method} @@ -3614,7 +3614,7 @@ $collection->all(); // ['Michael', 'Tom', 'Taylor'] ``` -For the inverse of `whenEmpty`, see the [`whenNotEmpty`](#method-whennotempty) method. +For the inverse of `whenEmpty`, see the [whenNotEmpty](#method-whennotempty) method. #### `whenNotEmpty()` {.collection-method} @@ -3659,7 +3659,7 @@ $collection->all(); // ['taylor'] ``` -For the inverse of `whenNotEmpty`, see the [`whenEmpty`](#method-whenempty) method. +For the inverse of `whenNotEmpty`, see the [whenEmpty](#method-whenempty) method. #### `where()` {.collection-method} @@ -3686,7 +3686,7 @@ $filtered->all(); */ ``` -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. +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. Supported operators are: '===', '!==', '!=', '==', '=', '<>', '>', '<', '>=', and '<=': @@ -3712,7 +3712,7 @@ $filtered->all(); #### `whereStrict()` {.collection-method} -This method has the same signature as the [`where`](#method-where) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [where](#method-where) method; however, all values are compared using "strict" comparisons. #### `whereBetween()` {.collection-method} @@ -3766,12 +3766,12 @@ $filtered->all(); */ ``` -The `whereIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereInStrict`](#method-whereinstrict) method to filter using "strict" comparisons. +The `whereIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [whereInStrict](#method-whereinstrict) method to filter using "strict" comparisons. #### `whereInStrict()` {.collection-method} -This method has the same signature as the [`whereIn`](#method-wherein) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [whereIn](#method-wherein) method; however, all values are compared using "strict" comparisons. #### `whereInstanceOf()` {.collection-method} @@ -3846,12 +3846,12 @@ $filtered->all(); */ ``` -The `whereNotIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereNotInStrict`](#method-wherenotinstrict) method to filter using "strict" comparisons. +The `whereNotIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [whereNotInStrict](#method-wherenotinstrict) method to filter using "strict" comparisons. #### `whereNotInStrict()` {.collection-method} -This method has the same signature as the [`whereNotIn`](#method-wherenotin) method; however, all values are compared using "strict" comparisons. +This method has the same signature as the [whereNotIn](#method-wherenotin) method; however, all values are compared using "strict" comparisons. #### `whereNotNull()` {.collection-method} @@ -3945,7 +3945,7 @@ $zipped->all(); ## Higher Order Messages -Collections also provide support for "higher order messages", which are short-cuts for performing common actions on collections. The collection methods that provide higher order messages are: [`average`](#method-average), [`avg`](#method-avg), [`contains`](#method-contains), [`each`](#method-each), [`every`](#method-every), [`filter`](#method-filter), [`first`](#method-first), [`flatMap`](#method-flatmap), [`groupBy`](#method-groupby), [`keyBy`](#method-keyby), [`map`](#method-map), [`max`](#method-max), [`min`](#method-min), [`partition`](#method-partition), [`reject`](#method-reject), [`skipUntil`](#method-skipuntil), [`skipWhile`](#method-skipwhile), [`some`](#method-some), [`sortBy`](#method-sortby), [`sortByDesc`](#method-sortbydesc), [`sum`](#method-sum), [`takeUntil`](#method-takeuntil), [`takeWhile`](#method-takewhile), and [`unique`](#method-unique). +Collections also provide support for "higher order messages", which are short-cuts for performing common actions on collections. The collection methods that provide higher order messages are: [average](#method-average), [avg](#method-avg), [contains](#method-contains), [each](#method-each), [every](#method-every), [filter](#method-filter), [first](#method-first), [flatMap](#method-flatmap), [groupBy](#method-groupby), [keyBy](#method-keyby), [map](#method-map), [max](#method-max), [min](#method-min), [partition](#method-partition), [reject](#method-reject), [skipUntil](#method-skipuntil), [skipWhile](#method-skipwhile), [some](#method-some), [sortBy](#method-sortby), [sortByDesc](#method-sortbydesc), [sum](#method-sum), [takeUntil](#method-takeuntil), [takeWhile](#method-takewhile), and [unique](#method-unique). Each higher order message can be accessed as a dynamic property on a collection instance. For instance, let's use the `each` higher order message to call a method on each object within a collection: From de0cc80cd74216e1c7a5ac5d5681165357f865a6 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:34:26 +0200 Subject: [PATCH 029/162] Fix link formatting (#10310) --- billing.md | 2 +- blade.md | 4 ++-- container.md | 2 +- eloquent.md | 2 +- hashing.md | 2 +- helpers.md | 10 +++++----- homestead.md | 2 +- mail.md | 2 +- notifications.md | 2 +- packages.md | 4 ++-- passwords.md | 2 +- pulse.md | 6 +++--- queries.md | 4 ++-- queues.md | 4 ++-- requests.md | 2 +- routing.md | 2 +- scout.md | 2 +- validation.md | 34 +++++++++++++++++----------------- vite.md | 12 ++++++------ 19 files changed, 50 insertions(+), 50 deletions(-) diff --git a/billing.md b/billing.md index 5f66343a02..e9a1cf243b 100644 --- a/billing.md +++ b/billing.md @@ -2446,7 +2446,7 @@ Route::get('/product-checkout', function (Request $request) { }); ``` -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. +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 diff --git a/blade.md b/blade.md index e599de1b96..acf37a67f3 100644 --- a/blade.md +++ b/blade.md @@ -1062,7 +1062,7 @@ If you need to merge other attributes onto your component, you can chain the `me ``` > [!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). +> 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 @@ -1926,7 +1926,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa ### Custom Echo Handlers -If you attempt to "echo" an object using Blade, the object's `__toString` method will be invoked. The [`__toString`](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) method is one of PHP's built-in "magic methods". However, sometimes you may not have control over the `__toString` method of a given class, such as when the class that you are interacting with belongs to a third-party library. +If you attempt to "echo" an object using Blade, the object's `__toString` method will be invoked. The [__toString](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) method is one of PHP's built-in "magic methods". However, sometimes you may not have control over the `__toString` method of a given class, such as when the class that you are interacting with belongs to a third-party library. In these cases, Blade allows you to register a custom echo handler for that particular type of object. To accomplish this, you should invoke Blade's `stringable` method. The `stringable` method accepts a closure. This closure should type-hint the type of object that it is responsible for rendering. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class: diff --git a/container.md b/container.md index c17e22070b..c884b64582 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`, `DB`, `Log`, `RouteParameter`, and [`Tag`](#tagging) attributes: +In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `DB`, `Log`, `RouteParameter`, and [Tag](#tagging) attributes: ```php ### Chunking Using Lazy Collections -The `lazy` method works similarly to [the `chunk` method](#chunking-results) in the sense that, behind the scenes, it executes the query in chunks. However, instead of passing each chunk directly into a callback as is, the `lazy` method returns a flattened [`LazyCollection`](/docs/{{version}}/collections#lazy-collections) of Eloquent models, which lets you interact with the results as a single stream: +The `lazy` method works similarly to [the `chunk` method](#chunking-results) in the sense that, behind the scenes, it executes the query in chunks. However, instead of passing each chunk directly into a callback as is, the `lazy` method returns a flattened [LazyCollection](/docs/{{version}}/collections#lazy-collections) of Eloquent models, which lets you interact with the results as a single stream: ```php use App\Models\Flight; diff --git a/hashing.md b/hashing.md index ff5fe2c2df..2210343cae 100644 --- a/hashing.md +++ b/hashing.md @@ -18,7 +18,7 @@ Bcrypt is a great choice for hashing passwords because its "work factor" is adju ## Configuration -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). +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: diff --git a/helpers.md b/helpers.md index 83d113dbe5..ffee8f24a9 100644 --- a/helpers.md +++ b/helpers.md @@ -2051,7 +2051,7 @@ blank(false); // false ``` -For the inverse of `blank`, see the [`filled`](#method-filled) method. +For the inverse of `blank`, see the [filled](#method-filled) method. #### `broadcast()` {.collection-method} @@ -2184,7 +2184,7 @@ dd($value); dd($value1, $value2, $value3, ...); ``` -If you do not want to halt the execution of your script, use the [`dump`](#method-dump) function instead. +If you do not want to halt the execution of your script, use the [dump](#method-dump) function instead. #### `dispatch()` {.collection-method} @@ -2215,7 +2215,7 @@ dump($value); dump($value1, $value2, $value3, ...); ``` -If you want to stop executing the script after dumping the variables, use the [`dd`](#method-dd) function instead. +If you want to stop executing the script after dumping the variables, use the [dd](#method-dd) function instead. #### `encrypt()` {.collection-method} @@ -2292,7 +2292,7 @@ filled(collect()); // false ``` -For the inverse of `filled`, see the [`blank`](#method-blank) method. +For the inverse of `filled`, see the [blank](#method-blank) method. #### `info()` {.collection-method} @@ -3241,7 +3241,7 @@ 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. +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. ### Timebox diff --git a/homestead.md b/homestead.md index bd282bc9d3..14c1755e06 100644 --- a/homestead.md +++ b/homestead.md @@ -692,7 +692,7 @@ Supported `policy` values include: `none`, `download`, `upload`, and `public`. ### Laravel Dusk -In order to run [Laravel Dusk](/docs/{{version}}/dusk) tests within Homestead, you should enable the [`webdriver` feature](#installing-optional-features) in your Homestead configuration: +In order to run [Laravel Dusk](/docs/{{version}}/dusk) tests within Homestead, you should enable the [webdriver feature](#installing-optional-features) in your Homestead configuration: ```yaml features: diff --git a/mail.md b/mail.md index 4958ddc3c3..8c627026d0 100644 --- a/mail.md +++ b/mail.md @@ -176,7 +176,7 @@ To utilize AWS [temporary credentials](https://docs.aws.amazon.com/IAM/latest/Us ], ``` -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: +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 /** diff --git a/notifications.md b/notifications.md index 65ddc93f75..4b3e92530d 100644 --- a/notifications.md +++ b/notifications.md @@ -920,7 +920,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 diff --git a/packages.md b/packages.md index d5bdb223d7..a6b4e50aa3 100644 --- a/packages.md +++ b/packages.md @@ -324,7 +324,7 @@ Blade will automatically detect the class that's linked to this component by pas #### 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 the [`loadViewsFrom` method](#views)). 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 @@ -373,7 +373,7 @@ public function boot(): void ### 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: +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: ```php /** diff --git a/passwords.md b/passwords.md index d12655a9a1..d9102bfe7e 100644 --- a/passwords.md +++ b/passwords.md @@ -37,7 +37,7 @@ By default, Laravel will respond to all requests it receives regardless of the c 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 method, 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 diff --git a/pulse.md b/pulse.md index f17d1839ff..215b7fb2b1 100644 --- a/pulse.md +++ b/pulse.md @@ -404,7 +404,7 @@ If no regular expression patterns match the request's URL, then the `'default'` #### Servers -The `Servers` recorder captures CPU, memory, and storage usage of the servers that power your application for display on the [Servers](#servers-card) card. This recorder requires the [`pulse:check` command](#capturing-entries) to be running on each of the servers you wish to monitor. +The `Servers` recorder captures CPU, memory, and storage usage of the servers that power your application for display on the [Servers](#servers-card) card. This recorder requires the [pulse:check command](#capturing-entries) to be running on each of the servers you wish to monitor. Each reporting server must have a unique name. By default, Pulse will use the value returned by PHP's `gethostname` function. If you wish to customize this, you may set the `PULSE_SERVER_NAME` environment variable: @@ -509,7 +509,7 @@ php artisan pulse:restart By default, Pulse will capture every relevant event that occurs in your application. For high-traffic applications, this can result in needing to aggregate millions of database rows in the dashboard, especially for longer time periods. -You may instead choose to enable "sampling" on certain Pulse data recorders. For example, setting the sample rate to `0.1` on the [`User Requests`](#user-requests-recorder) recorder will mean that you only record approximately 10% of the requests to your application. In the dashboard, the values will be scaled up and prefixed with a `~` to indicate that they are an approximation. +You may instead choose to enable "sampling" on certain Pulse data recorders. For example, setting the sample rate to `0.1` on the [User Requests](#user-requests-recorder) recorder will mean that you only record approximately 10% of the requests to your application. In the dashboard, the values will be scaled up and prefixed with a `~` to indicate that they are an approximation. In general, the more entries you have for a particular metric, the lower you can safely set the sample rate without sacrificing too much accuracy. @@ -671,7 +671,7 @@ You may then specify the configuration file in your CSS entrypoint: @tailwind utilities; ``` -You will also need to include an `id` or `class` attribute in your card's view that matches the selector passed to Tailwind's [`important` selector strategy](https://tailwindcss.com/docs/configuration#selector-strategy): +You will also need to include an `id` or `class` attribute in your card's view that matches the selector passed to Tailwind's [important selector strategy](https://tailwindcss.com/docs/configuration#selector-strategy): ```blade diff --git a/queries.md b/queries.md index 70b0a0ec5a..da2e2e1011 100644 --- a/queries.md +++ b/queries.md @@ -203,7 +203,7 @@ DB::table('users')->where(function ($query) { ### Streaming Results Lazily -The `lazy` method works similarly to [the `chunk` method](#chunking-results) in the sense that it executes the query in chunks. However, instead of passing each chunk into a callback, the `lazy()` method returns a [`LazyCollection`](/docs/{{version}}/collections#lazy-collections), which lets you interact with the results as a single stream: +The `lazy` method works similarly to [the chunk method](#chunking-results) in the sense that it executes the query in chunks. However, instead of passing each chunk into a callback, the `lazy()` method returns a [LazyCollection](/docs/{{version}}/collections#lazy-collections), which lets you interact with the results as a single stream: ```php use Illuminate\Support\Facades\DB; @@ -1194,7 +1194,7 @@ $users = DB::table('users') ->get(); ``` -To build more advanced `having` statements, see the [`havingRaw`](#raw-methods) method. +To build more advanced `having` statements, see the [havingRaw](#raw-methods) method. ### Limit and Offset diff --git a/queues.md b/queues.md index 48e185a88c..e98b257d54 100644 --- a/queues.md +++ b/queues.md @@ -376,7 +376,7 @@ class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique ``` > [!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. +> 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 @@ -523,7 +523,7 @@ public function middleware(): array } ``` -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. +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: diff --git a/requests.md b/requests.md index 9611fedd8c..01fe612622 100644 --- a/requests.md +++ b/requests.md @@ -370,7 +370,7 @@ $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): +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): ```php $name = $request->string('name')->trim(); diff --git a/routing.md b/routing.md index a0273378b3..cb390ec189 100644 --- a/routing.md +++ b/routing.md @@ -466,7 +466,7 @@ $url = route('profile', ['id' => 1, '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). +> 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 diff --git a/scout.md b/scout.md index 346be12537..81772e46a9 100644 --- a/scout.md +++ b/scout.md @@ -754,7 +754,7 @@ $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: +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: ```php $orders = Order::search('Star Trek') diff --git a/validation.md b/validation.md index 781d62338b..46b4445d60 100644 --- a/validation.md +++ b/validation.md @@ -1201,7 +1201,7 @@ use Illuminate\Validation\Rule; #### alpha -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=). +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: @@ -1212,7 +1212,7 @@ To restrict this validation rule to characters in the ASCII range (`a-z` and `A- #### alpha_dash -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 (`_`). +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`, `A-Z`, and `0-9`), you may provide the `ascii` option to the validation rule: @@ -1223,7 +1223,7 @@ To restrict this validation rule to characters in the ASCII range (`a-z`, `A-Z`, #### alpha_num -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=). +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`, `A-Z`, and `0-9`), you may provide the `ascii` option to the validation rule: @@ -1277,7 +1277,7 @@ if ($validator->stopOnFirstFailure()->fails()) { #### before:_date_ -The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function in 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`. +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: @@ -1302,7 +1302,7 @@ The `beforeToday` and `todayOrBefore` methods may be used to fluently express th #### 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`. +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: @@ -1318,7 +1318,7 @@ use Illuminate\Validation\Rule; #### between:_min_,_max_ -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. +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 @@ -1478,7 +1478,7 @@ The field under validation must not end with one of the given values. #### email -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: +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: ```php 'email' => 'email:rfc,dns' @@ -1667,7 +1667,7 @@ The file under validation must have a user-assigned extension corresponding to o ``` > [!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. +> 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. #### file @@ -1682,12 +1682,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_ 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. +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_ 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. +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. #### hex_color @@ -1774,12 +1774,12 @@ The field under validation must be a valid JSON string. #### lt:_field_ -The field under validation must be less 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 less 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. #### lte:_field_ -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. +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 @@ -1799,7 +1799,7 @@ The field under validation must be a MAC address. #### max:_value_ -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. +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_ @@ -1833,12 +1833,12 @@ Even though you only need to specify the extensions, this rule actually validate #### MIME Types and Extensions -This validation rule does not verify agreement between the MIME type and the extension the user assigned to the file. For example, the `mimes:png` validation rule would consider a file containing valid PNG content to be a valid PNG image, even if the file is named `photo.txt`. If you would like to validate the user-assigned extension of the file, you may use the [`extensions`](#rule-extensions) rule. +This validation rule does not verify agreement between the MIME type and the extension the user assigned to the file. For example, the `mimes:png` validation rule would consider a file containing valid PNG content to be a valid PNG image, even if the file is named `photo.txt`. If you would like to validate the user-assigned extension of the file, you may use the [extensions](#rule-extensions) rule. #### min:_value_ -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. +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_ @@ -2374,7 +2374,7 @@ Like the `$input` parameter passed to the closure, the `$item` parameter is an i ## Validating Arrays -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: +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: ```php use Illuminate\Support\Facades\Validator; @@ -2827,7 +2827,7 @@ $validator = Validator::make($request->all(), [ ### 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: +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: ```php use Illuminate\Support\Facades\Validator; diff --git a/vite.md b/vite.md index 23a4050c61..2611b84763 100644 --- a/vite.md +++ b/vite.md @@ -175,7 +175,7 @@ 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. +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 @@ -195,7 +195,7 @@ export default defineConfig({ }); ``` -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). +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 @@ -525,7 +525,7 @@ export default defineConfig({ }); ``` -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: +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'; @@ -766,7 +766,7 @@ php artisan inertia:start-ssr ### 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): +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 ### 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-sri`](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 @@ -853,7 +853,7 @@ 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): +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; From 29d8413031e18e97bd5ad9070a50bd20b741a982 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:02:51 +0200 Subject: [PATCH 030/162] Use single quotes for string consistency (#10312) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index e318e3401c..e2ff7fd9a7 100644 --- a/collections.md +++ b/collections.md @@ -630,7 +630,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 (string $email) { - return substr(strrchr($email, "@"), 1); + return substr(strrchr($email, '@'), 1); }); $counted->all(); From f81d2de96d0d050a8d38ce925b7e56b358dc00d5 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:19:21 +0200 Subject: [PATCH 031/162] Improve consistency in countBy method description (#10313) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index e2ff7fd9a7..d12d78451d 100644 --- a/collections.md +++ b/collections.md @@ -624,7 +624,7 @@ $counted->all(); // [1 => 1, 2 => 3, 3 => 1] ``` -You pass a closure to the `countBy` method to count all items by a custom value: +You may pass a closure to the `countBy` method to count all items by a custom value: ```php $collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']); From 61ea2b95af68cd176d6259e887d16487353b5cf3 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 8 Apr 2025 00:50:58 +1000 Subject: [PATCH 032/162] [12.x] Document `tap` and `pipe` methods on the query builder (#10303) * Query scopes for `tap` and `pipe` * Add toc item * formatting --------- Co-authored-by: Taylor Otwell --- queries.md | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/queries.md b/queries.md index da2e2e1011..d58280c26f 100644 --- a/queries.md +++ b/queries.md @@ -33,6 +33,7 @@ - [Increment and Decrement](#increment-and-decrement) - [Delete Statements](#delete-statements) - [Pessimistic Locking](#pessimistic-locking) +- [Reusable Query Components](#reusable-query-components) - [Debugging](#debugging) @@ -1468,6 +1469,131 @@ DB::transaction(function () { }); ``` + +## Reusable Query Components + +If you have repeated query logic throughout your application, you may extract the logic into reusable objects using the query builder's `tap` and `pipe` methods. Imagine you have these two different queries in your application: + +```php +use Illuminate\Database\Query\Builder; +use Illuminate\Support\Facades\DB; + +$destination = $request->query('destination'); + +DB::table('flights') + ->when($destination, function (Builder $query, string $destination) { + $query->where('destination', $destination); + }) + ->orderByDesc('price') + ->get(); + +// ... + +$destination = $request->query('destination'); + +DB::table('flights') + ->when($destination, function (Builder $query, string $destination) { + $query->where('destination', $destination); + }) + ->where('user', $request->user()->id) + ->orderBy('destination') + ->get(); +``` + +You may like to extract the destination filtering that is common between the queries into a reusable object: + +```php +when($this->destination, function (Builder $query) { + $query->where('destination', $this->destination); + }); + } +} +``` + +Then, you can use the query builder's `tap` method to apply the object's logic to the query: + +```php +use App\Scopes\DestinationFilter; +use Illuminate\Database\Query\Builder; +use Illuminate\Support\Facades\DB; + +DB::table('flights') + ->when($destination, function (Builder $query, string $destination) { // [tl! remove] + $query->where('destination', $destination); // [tl! remove] + }) // [tl! remove] + ->tap(new DesinationFilter($destination)) // [tl! add] + ->orderByDesc('price') + ->get(); + +// ... + +DB::table('flights') + ->when($destination, function (Builder $query, string $destination) { // [tl! remove] + $query->where('destination', $destination); // [tl! remove] + }) // [tl! remove] + ->tap(new DesinationFilter($destination)) // [tl! add] + ->where('user', $request->user()->id) + ->orderBy('destination') + ->get(); +``` + + +#### Query Pipes + +The `tap` method will always return the query builder. If you would like to extract an object that executes the query and returns another value, you may use the `pipe` method instead. + +Consider the following query object that contains shared [pagination](/docs/{{version}}/pagination) logic used throughout an application. Unlike the `DesinationFilter`, which applies query conditions to the query, the `Paginate` object executes the query and returns a paginator instance: + +```php +orderBy($this->sortBy, $this->sortDirection) + ->paginate($this->perPage, pageName: 'p'); + } +} +``` + +Using the query builder's `pipe` method, we can leverage this object to apply our shared pagination logic: + +```php +$flights = DB::table('flights') + ->tap(new DesinationFilter($destination)) + ->pipe(new Paginate); +``` + ## Debugging From 3d9881a46ef8994bd649eed22bf05c70cc40d21c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:51:40 +0200 Subject: [PATCH 033/162] Fix implode method examples to show actual return values (#10314) --- collections.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index d12d78451d..9cf8a828d5 100644 --- a/collections.md +++ b/collections.md @@ -1421,7 +1421,7 @@ $collection = collect([ $collection->implode('product', ', '); -// Desk, Chair +// 'Desk, Chair' ``` If the collection contains simple strings or numeric values, you should pass the "glue" as the only argument to the method: @@ -1439,7 +1439,7 @@ $collection->implode(function (array $item, int $key) { return strtoupper($item['product']); }, ', '); -// DESK, CHAIR +// 'DESK, CHAIR' ``` From 2e1efb9ed388c2fd93902a1911232f1f5c3203ea Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 8 Apr 2025 09:20:19 -0500 Subject: [PATCH 034/162] Update Paginator / LengthAwarePaginator Instance Methods Table (#10317) * Update pagination.md `onLastPage()` is available to the `LengthAwarePaginator` as well, via the `AbstractPaginator` class. * Update pagination.md --- pagination.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pagination.md b/pagination.md index 42c6ac57ad..0fa0840e4f 100644 --- a/pagination.md +++ b/pagination.md @@ -376,6 +376,7 @@ Each paginator instance provides additional pagination information via the follo | `$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`). | From 89f67d377c5fc4ebee88d764fc8fd6d76e0abb4f Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:11:40 +0200 Subject: [PATCH 035/162] [12.x] Document `fromJson` method on `collections` docs (#10316) * Document fromJson method on collections docs * formatting --------- Co-authored-by: Taylor Otwell --- collections.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/collections.md b/collections.md index 9cf8a828d5..d1a11fe3c3 100644 --- a/collections.md +++ b/collections.md @@ -35,6 +35,8 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); ``` +You may also create a collection using the [make](#method-make) and [fromJson](#method-fromjson) methods. + > [!NOTE] > The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. @@ -144,6 +146,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [flip](#method-flip) [forget](#method-forget) [forPage](#method-forpage) +[fromJson](#method-fromjson) [get](#method-get) [groupBy](#method-groupby) [has](#method-has) @@ -1246,6 +1249,23 @@ $chunk->all(); // [4, 5, 6] ``` + +#### `fromJson()` {.collection-method} + +The static `fromJson` method creates a new collection instance by decoding a given JSON string using the `json_decode` PHP function: + +```php +use Illuminate\Support\Collection; + +$json = json_encode([ + 'name' => 'Taylor Otwell', + 'role' => 'Developer', + 'status' => 'Active', +]); + +$collection = Collection::fromJson($json); +``` + #### `get()` {.collection-method} @@ -1697,6 +1717,12 @@ The static `macro` method allows you to add methods to the `Collection` class at The static `make` method creates a new collection instance. See the [Creating Collections](#creating-collections) section. +```php +use Illuminate\Support\Collection; + +$collection = Collection::make([1, 2, 3]); +``` + #### `map()` {.collection-method} From 217229aad8c4ed7ac5e5c6fb494aa21586810951 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Apr 2025 14:16:44 -0500 Subject: [PATCH 036/162] document failed request --- http-client.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 9e884014cd..42fe489d24 100644 --- a/http-client.md +++ b/http-client.md @@ -627,7 +627,7 @@ Http::fake([ ``` -#### Faking Connection Exceptions +#### Faking 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: @@ -637,6 +637,14 @@ Http::fake([ ]); ``` +To test your application's behavior if a `Illuminate\Http\Client\RequestException` is thrown, you may use the `failedRequest` method: + +```php +Http::fake([ + 'github.com/*' => Http::failedRequest(['code' => 'not_found'], 404), +]); +``` + #### Faking Response Sequences From 72904db99eea2043515447562af05908f82aa5c2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Apr 2025 14:22:51 -0500 Subject: [PATCH 037/162] document whereAttachedTo --- eloquent-relationships.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index a75adf48ad..8ed27fdc58 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1642,6 +1642,23 @@ $posts = Post::whereHas('comments', function (Builder $query) { > [!WARNING] > Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. + +#### Many to Many Relationship Existence Queries + +The `whereAttachedTo` method may be used to query for models that have a many to many attachment to a model or collection of models: + +```php +$users = User::whereAttachedTo($role)->get(); +``` + +You may also provide a [collection](/docs/{{version}}/eloquent-collections) instance to the `whereAttachedTo` method. When doing so, Laravel will retrieve models that are attached to any of the models within the collection: + +```php +$tags = Tag::whereLike('name', '%laravel%')->get(); + +$posts = Post::whereAttachedTo($tags)->get(); +``` + #### Inline Relationship Existence Queries From 7457dd713eecf70630dca0014df9bbbfb7b1272c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Apr 2025 14:29:09 -0500 Subject: [PATCH 038/162] document any of --- validation.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/validation.md b/validation.md index 46b4445d60..501f2cb7f0 100644 --- a/validation.md +++ b/validation.md @@ -1096,6 +1096,7 @@ Below is a list of all available validation rules and their function:
+[Any Of](#rule-anyof) [Bail](#rule-bail) [Exclude](#rule-exclude) [Exclude If](#rule-exclude-if) @@ -1198,6 +1199,23 @@ use Illuminate\Validation\Rule; ], ``` + +#### anyOf + +The `Rule::anyOf` validation rule allows you to specify that the field under validation must satisfy any of the given validation rulesets. For example, the following rule will validate that the `username` field is either an email address or an alpha-numeric string (including dashes) that is at least 6 characters long: + +```php +use Illuminate\Validation\Rule; + +'username' => [ + 'required', + Rule::anyOf([ + ['string', 'email'], + ['string', 'alpha_dash', 'min:6'], + ]), +], +``` + #### alpha From 320e31007b6859f4664e339e2013d0e2409de961 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Apr 2025 14:56:12 -0500 Subject: [PATCH 039/162] document auto discovery of resources --- eloquent-resources.md | 61 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index 638d5576a2..f2e0b55501 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -90,6 +90,14 @@ Route::get('/user/{id}', function (string $id) { }); ``` +For convenience, you may use the model's `toResource` method, which will use framework conventions to automatically discover the model's underlying resource: + +```php +return User::findOrFail($id)->toResource(); +``` + +When invoking the `toResource` method, Laravel will attempt to locate a resource that matches the model's name and is optionally suffixed with `Resource` within the `Http\Resources` namespace closest to the model's namespace. + ### Resource Collections @@ -104,7 +112,18 @@ Route::get('/users', function () { }); ``` -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: +Or, for convenience, you may use the Eloquent collection's `toResourceCollection` method, which will use framework conventions to automatically discover the model's underlying resource collection: + +```php +return User::all()->toResourceCollection(); +``` + +When invoking the `toResourceCollection` method, Laravel will attempt to locate a resource collection that matches the model's name and is suffixed with `Collection` within the `Http\Resources` namespace closest to the model's namespace. + + +#### Custom Resource Collections + +By default, resource collections do 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: ```shell php artisan make:resource UserCollection @@ -150,6 +169,14 @@ Route::get('/users', function () { }); ``` +Or, for convenience, you may use the Eloquent collection's `toResourceCollection` method, which will use framework conventions to automatically discover the model's underlying resource collection: + +```php +return User::all()->toResourceCollection(); +``` + +When invoking the `toResourceCollection` method, Laravel will attempt to locate a resource collection that matches the model's name and is suffixed with `Collection` within the `Http\Resources` namespace closest to the model's namespace. + #### Preserving Collection Keys @@ -248,11 +275,10 @@ class UserResource extends JsonResource 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)); + return User::findOrFail($id)->toUserResource(); }); ``` @@ -289,14 +315,13 @@ public function toArray(Request $request): array #### Resource Collections -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: +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 Eloquent model collections provide a `toResourceCollection` method to generate an "ad-hoc" resource collection on the fly: ```php -use App\Http\Resources\UserResource; use App\Models\User; Route::get('/users', function () { - return UserResource::collection(User::all()); + return User::all()->toResourceCollection(); }); ``` @@ -340,6 +365,14 @@ Route::get('/users', function () { }); ``` +Or, for convenience, you may use the Eloquent collection's `toResourceCollection` method, which will use framework conventions to automatically discover the model's underlying resource collection: + +```php +return User::all()->toResourceCollection(); +``` + +When invoking the `toResourceCollection` method, Laravel will attempt to locate a resource collection that matches the model's name and is suffixed with `Collection` within the `Http\Resources` namespace closest to the model's namespace. + ### Data Wrapping @@ -474,6 +507,12 @@ Route::get('/users', function () { }); ``` +Or, for convenience, you may use the paginator's `toResourceCollection` method, which will use framework conventions to automatically discover the paginated model's underlying resource collection: + +```php +return User::paginate()->toResourceCollection(); +``` + Paginated responses always contain `meta` and `links` keys with information about the paginator's state: ```json @@ -800,7 +839,9 @@ class UserCollection extends ResourceCollection 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: ```php -return (new UserCollection(User::all()->load('roles'))) +return User::all() + ->load('roles') + ->toResourceCollection() ->additional(['meta' => [ 'key' => 'value', ]]); @@ -812,11 +853,10 @@ return (new UserCollection(User::all()->load('roles'))) As you have already read, resources may be returned directly from routes and controllers: ```php -use App\Http\Resources\UserResource; use App\Models\User; Route::get('/user/{id}', function (string $id) { - return new UserResource(User::findOrFail($id)); + return User::findOrFail($id)->toResource(); }); ``` @@ -827,7 +867,8 @@ use App\Http\Resources\UserResource; use App\Models\User; Route::get('/user', function () { - return (new UserResource(User::find(1))) + return User::find(1) + ->toResource() ->response() ->header('X-Value', 'True'); }); From 0b78bb4da2914b6100666eced007d40b34284816 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Apr 2025 15:10:51 -0500 Subject: [PATCH 040/162] automatic eager loading --- eloquent-relationships.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 8ed27fdc58..50eb9ccb2d 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -33,6 +33,7 @@ - [Eager Loading](#eager-loading) - [Constraining Eager Loads](#constraining-eager-loads) - [Lazy Eager Loading](#lazy-eager-loading) + - [Automatic Eager Loading](#automatic-eager-loading) - [Preventing Lazy Loading](#preventing-lazy-loading) - [Inserting and Updating Related Models](#inserting-and-updating-related-models) - [The `save` Method](#the-save-method) @@ -2244,6 +2245,44 @@ $activities = ActivityFeed::with('parentable') ]); ``` + +### Automatic Eager Loading + +> [!WARNING] +> This feature is currently in beta in order to gather community feedback. The behavior and functionality of this feature may change even on patch releases. + +In many cases, Laravel can automatically eager load the relationships you access. To enable automatic eager loading, you should invoke the `Model::automaticallyEagerLoadRelationships` method within the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Database\Eloquent\Model; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Model::automaticallyEagerLoadRelationships(); +} +``` + +When this feature is enabled, Laravel will attempt to automatically load any relationships you access that have not been previously loaded. For example, consider the following scenario: + +```php +use App\Models\User; + +$users = User::all(); + +foreach ($users as $user) { + foreach ($user->posts as $post) { + foreach ($post->comments as $comment) { + echo $comment->content; + } + } +} +``` + +Typically, the code above would execute a query for each user in order to retrieve their posts, as well as a query for each post to retrieve its comments. However, when the `automaticallyEagerLoadRelationships` feature has been enabled, Laravel will automatically [lazy eager load](#lazy-eager-loading) the posts for all users in the user collection when you attempt to access the posts on any of the retrieved users. Likewise, when you attempt to access the comments for any retrieved post, all comments will be lazy eager loaded for all posts that were originally retrieved. + ### Preventing Lazy Loading From b6829f947a084978934266cce2d02b6289499aba Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 9 Apr 2025 09:10:38 -0500 Subject: [PATCH 041/162] remove bold --- collections.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/collections.md b/collections.md index d1a11fe3c3..102c85b363 100644 --- a/collections.md +++ b/collections.md @@ -512,7 +512,7 @@ $combined->all(); #### `concat()` {.collection-method} -The `concat` method appends the given `array` or collection's values onto the end of another collection: +The `concat` method appends the given array or collection's values onto the end of another collection: ```php $collection = collect(['John Doe']); @@ -1465,7 +1465,7 @@ $collection->implode(function (array $item, int $key) { #### `intersect()` {.collection-method} -The `intersect` method removes any values from the original collection that are not present in the given `array` or collection. The resulting collection will preserve the original collection's keys: +The `intersect` method removes any values from the original collection that are not present in the given array or collection. The resulting collection will preserve the original collection's keys: ```php $collection = collect(['Desk', 'Sofa', 'Chair']); @@ -1483,7 +1483,7 @@ $intersect->all(); #### `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: +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: ```php $collection = collect(['Desk', 'Sofa', 'Chair']); @@ -1500,7 +1500,7 @@ $intersect->all(); #### `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: +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: ```php $collection = collect([ @@ -1523,7 +1523,7 @@ $intersect->all(); #### `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: +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: ```php $collection = collect([ @@ -1548,7 +1548,7 @@ $intersect->all(); #### `intersectByKeys()` {.collection-method} -The `intersectByKeys` method removes any keys and their corresponding values from the original collection that are not present in the given `array` or collection: +The `intersectByKeys` method removes any keys and their corresponding values from the original collection that are not present in the given array or collection: ```php $collection = collect([ From a8d4db9ae393f52a9c1639b4cf5e43fbe2014a02 Mon Sep 17 00:00:00 2001 From: Simone Folador Date: Wed, 9 Apr 2025 18:10:26 +0200 Subject: [PATCH 042/162] Update queries.md (#10320) corrected mispelling of "destination" --- queries.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/queries.md b/queries.md index d58280c26f..5c731ebcad 100644 --- a/queries.md +++ b/queries.md @@ -1509,10 +1509,10 @@ namespace App\Scopes; use Illuminate\Database\Query\Builder; -class DesinationFilter +class DestinationFilter { public function __construct( - private ?string $desination, + private ?string $destination, ) { // } @@ -1537,7 +1537,7 @@ DB::table('flights') ->when($destination, function (Builder $query, string $destination) { // [tl! remove] $query->where('destination', $destination); // [tl! remove] }) // [tl! remove] - ->tap(new DesinationFilter($destination)) // [tl! add] + ->tap(new DestinationFilter($destination)) // [tl! add] ->orderByDesc('price') ->get(); @@ -1547,7 +1547,7 @@ DB::table('flights') ->when($destination, function (Builder $query, string $destination) { // [tl! remove] $query->where('destination', $destination); // [tl! remove] }) // [tl! remove] - ->tap(new DesinationFilter($destination)) // [tl! add] + ->tap(new DestinationFilter($destination)) // [tl! add] ->where('user', $request->user()->id) ->orderBy('destination') ->get(); @@ -1558,7 +1558,7 @@ DB::table('flights') The `tap` method will always return the query builder. If you would like to extract an object that executes the query and returns another value, you may use the `pipe` method instead. -Consider the following query object that contains shared [pagination](/docs/{{version}}/pagination) logic used throughout an application. Unlike the `DesinationFilter`, which applies query conditions to the query, the `Paginate` object executes the query and returns a paginator instance: +Consider the following query object that contains shared [pagination](/docs/{{version}}/pagination) logic used throughout an application. Unlike the `DestinationFilter`, which applies query conditions to the query, the `Paginate` object executes the query and returns a paginator instance: ```php tap(new DesinationFilter($destination)) + ->tap(new DestinationFilter($destination)) ->pipe(new Paginate); ``` From 52e15bdfbf5291b70a000dfb640f8ee93be9b74c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 9 Apr 2025 22:49:11 +0100 Subject: [PATCH 043/162] Fixes missing `return` type --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 5c731ebcad..f0d12effda 100644 --- a/queries.md +++ b/queries.md @@ -1580,7 +1580,7 @@ class Paginate public function __invoke(Builder $query): LengthAwarePaginator { - $query->orderBy($this->sortBy, $this->sortDirection) + return $query->orderBy($this->sortBy, $this->sortDirection) ->paginate($this->perPage, pageName: 'p'); } } From a77d8e7521b897b31d924434fef0ef6d1f73a909 Mon Sep 17 00:00:00 2001 From: Ostap Brehin Date: Thu, 10 Apr 2025 01:29:46 +0100 Subject: [PATCH 044/162] Replace `die` with `dd` (#10322) --- container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.md b/container.md index c884b64582..460a8a3f8d 100644 --- a/container.md +++ b/container.md @@ -74,7 +74,7 @@ class Service } Route::get('/', function (Service $service) { - die($service::class); + dd($service::class); }); ``` From 47fed28dbee4e7e051f71c7c6935d67f07eb05a7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 10 Apr 2025 07:53:11 -0500 Subject: [PATCH 045/162] wip --- eloquent-relationships.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 50eb9ccb2d..a298c655c5 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -2283,6 +2283,14 @@ foreach ($users as $user) { Typically, the code above would execute a query for each user in order to retrieve their posts, as well as a query for each post to retrieve its comments. However, when the `automaticallyEagerLoadRelationships` feature has been enabled, Laravel will automatically [lazy eager load](#lazy-eager-loading) the posts for all users in the user collection when you attempt to access the posts on any of the retrieved users. Likewise, when you attempt to access the comments for any retrieved post, all comments will be lazy eager loaded for all posts that were originally retrieved. +If you do not want to globally enable automatic eager loading, you can still enable this feature for a single Eloquent collection instance by invoking the `withRelationshipAutoloading` method on the collection: + +```php +$users = User::where('vip', true)->get(); + +return $users->withRelationshipAutoloading(); +``` + ### Preventing Lazy Loading From 9f8f242902b1429d8b9cf04a9a4b6867872d31e2 Mon Sep 17 00:00:00 2001 From: Punyapal Shah <53343069+MrPunyapal@users.noreply.github.com> Date: Fri, 11 Apr 2025 21:45:30 +0530 Subject: [PATCH 046/162] fixed the incorrect output in scope context example (#10323) --- context.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/context.md b/context.md index 39e60dfba9..fb473af114 100644 --- a/context.md +++ b/context.md @@ -188,7 +188,9 @@ Context::scope( ); Context::all(); -// [] +// [ +// 'trace_id' => 'abc-999', +// ] Context::allHidden(); // [ From e98ac6bec48caadcaa3519922cb1ae4f959c6dd1 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 15 Apr 2025 17:20:22 +0200 Subject: [PATCH 047/162] [12.x] Document the optional argument on database transactions (#10321) * Document the optional argument on database transactions * Update database.md --------- Co-authored-by: Taylor Otwell --- database.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/database.md b/database.md index 14c9c68adb..40f5a06b17 100644 --- a/database.md +++ b/database.md @@ -379,6 +379,20 @@ DB::transaction(function () { }); ``` +You may optionally pass a closure to the `transaction` method that will be executed if the transaction fails: + +```php +use Illuminate\Support\Facades\DB; + +DB::transaction(function () { + DB::update('update users set votes = 1'); + + DB::delete('delete from posts'); +}, onFailure: function () { + // ... +}); +``` + #### Handling Deadlocks From cec5ac61ad6b4e98b054d7a389c6ada4f5812c4b Mon Sep 17 00:00:00 2001 From: AJ <60591772+devajmeireles@users.noreply.github.com> Date: Tue, 15 Apr 2025 12:21:58 -0300 Subject: [PATCH 048/162] [12.x] Updating `packages.md` (#10329) * Added instructions for publishing all files with '--provider' flag in packages.md * Update packages.md --------- Co-authored-by: Taylor Otwell --- packages.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages.md b/packages.md index a6b4e50aa3..bce6bb978a 100644 --- a/packages.md +++ b/packages.md @@ -439,3 +439,9 @@ Now your users may publish these groups separately by referencing their tag when ```shell php artisan vendor:publish --tag=courier-config ``` + +Your users can also publish all publishable files defined by your package's service provider using the `--provider` flag: + +```shell +php artisan vendor:publish --provider="Your\Package\ServiceProvider" +``` From da87313a6da29791ba7ff004bd50d045b4595401 Mon Sep 17 00:00:00 2001 From: Zep Fietje Date: Tue, 15 Apr 2025 17:22:20 +0200 Subject: [PATCH 049/162] Update notifications.md (#10328) --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 4b3e92530d..33854ff559 100644 --- a/notifications.md +++ b/notifications.md @@ -292,7 +292,7 @@ use Illuminate\Queue\Middleware\RateLimited; public function middleware(object $notifiable, string $channel) { return match ($channel) { - 'email' => [new RateLimited('postmark')], + 'mail' => [new RateLimited('postmark')], 'slack' => [new RateLimited('slack')], default => [], }; From a3f59210ac44e9484590ec795c70e75b94566256 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Apr 2025 11:05:06 -0500 Subject: [PATCH 050/162] memo docs --- cache.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/cache.md b/cache.md index feed3275b6..225f0c008a 100644 --- a/cache.md +++ b/cache.md @@ -8,6 +8,7 @@ - [Retrieving Items From the Cache](#retrieving-items-from-the-cache) - [Storing Items in the Cache](#storing-items-in-the-cache) - [Removing Items From the Cache](#removing-items-from-the-cache) + - [Cache Memoization](#cache-memoization) - [The Cache Helper](#the-cache-helper) - [Atomic Locks](#atomic-locks) - [Managing Locks](#managing-locks) @@ -325,6 +326,50 @@ Cache::flush(); > [!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. + +### Cache Memoization + +Laravel's `memo` cache driver allows you to temporarily store resolved cache values in memory during a single request or job execution. This prevents repeated cache hits within the same execution, significantly improving performance. + +To use the memoized cache, invoke the `memo` method: + +```php +use Illuminate\Support\Facades\Cache; + +$value = Cache::memo()->get('key'); +``` + +The `memo` method optionally accepts the name of a cache store, which specifies the underlying cache store the memoized driver will decorate: + +```php +// Using the default cache store... +$value = Cache::memo()->get('key'); + +// Using the Redis cache store... +$value = Cache::memo('redis')->get('key'); +``` + +The first `get` call for a given key retrieves the value from your cache store, but subsequent calls within the same request or job will retrieve the value from memory: + +```php +// Hits the cache... +Cache::memo()->get('key'); + +// Does not hit the cache, returns memoized value... +Cache::memo()->get('key'); +``` + +When calling methods that modify cache values (such as `put`, `increment`, `remember`, etc.), the memoized cache automatically forgets the memoized value and delegates the mutating method call to the underlying cache store: + +```php +Cache::memo()->put('name', 'Taylor'); // Writes to underlying cache... +Cache::memo()->get('name'); // Hits underlying cache... +Cache::memo()->get('name'); // Memoized, does not hit cache... + +Cache::memo()->put('name', 'Tim'); // Forgets memoized value, writes new value... +Cache::memo()->get('name'); // Hits underlying cache again... +``` + ### The Cache Helper From 694c3b5ee4e580f45382ebae8ddb35b676c7121e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Apr 2025 13:02:22 -0500 Subject: [PATCH 051/162] wip --- cache.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache.md b/cache.md index 225f0c008a..de346c27da 100644 --- a/cache.md +++ b/cache.md @@ -353,10 +353,10 @@ The first `get` call for a given key retrieves the value from your cache store, ```php // Hits the cache... -Cache::memo()->get('key'); +$value = Cache::memo()->get('key'); // Does not hit the cache, returns memoized value... -Cache::memo()->get('key'); +$value = Cache::memo()->get('key'); ``` When calling methods that modify cache values (such as `put`, `increment`, `remember`, etc.), the memoized cache automatically forgets the memoized value and delegates the mutating method call to the underlying cache store: From 1c5372f53d08fc72c88091099e5f7d3be85b4d8d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Apr 2025 15:34:36 -0500 Subject: [PATCH 052/162] document uri --- helpers.md | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/helpers.md b/helpers.md index ffee8f24a9..7e60698175 100644 --- a/helpers.md +++ b/helpers.md @@ -10,6 +10,7 @@ - [Pipeline](#pipeline) - [Sleep](#sleep) - [Timebox](#timebox) + - [URI](#uri) ## Introduction @@ -3261,3 +3262,107 @@ use Illuminate\Support\Timebox; ``` If an exception is thrown within the closure, this class will respect the defined delay and re-throw the exception after the delay. + + +### URI + +Laravel's `Uri` class provides a convenient and fluent interface for creating and manipulating URIs. This class wraps the functionality provided by the underlying League URI package and integrates seamlessly with Laravel's routing system. + +You can create a `Uri` instance easily using static methods: + +```php +use App\Http\Controllers\UserController; +use App\Http\Controllers\InvokableController; +use Illuminate\Support\Uri; + +// Generate a URI instance from the given string... +$uri = Uri::of('https://example.com/path'); + +// Generate URI instances to paths, named routes, or controller actions... +$uri = Uri::to('/dashboard'); +$uri = Uri::route('user.profile', ['user' => 1]); +$uri = Uri::action([UserController::class, 'index']); +$uri = Uri::action(InvokableController::class); +``` + +Once you have a URI instance, you can fluently modify it: + +```php +$uri = Uri::of('https://example.com') + ->withScheme('http') + ->withHost('test.com') + ->withPort(8000) + ->withPath('/users') + ->withQuery(['page' => 2]) + ->withFragment('section-1'); +``` + + +#### Inspecting URIs + +The `Uri` class also allows you to easily inspect the various components of the underlying URI: + +```php +$scheme = $uri->scheme(); +$host = $uri->host(); +$port = $uri->port(); +$path = $uri->path(); +$query = $uri->query(); +$fragment = $uri->fragment(); +``` + + +#### Manipulating Query Strings + +The `Uri` class offers several methods that may be used to manipulate a URI's query string. The `withQuery` method may be used to merge additional query string parameters into the existing query string: + +```php +$uri = $uri->withQuery(['sort' => 'name']); +``` + +The `withQueryIfMissing` method may be used to merge additional query string parameters into the existing query string if the given keys do not already exist in the query string: + +```php +$uri = $uri->withQueryIfMissing(['page' => 1]); +``` + +The `replaceQuery` method may be used to complete replace the existing query string with a new one: + +```php +$uri = $uri->replaceQuery(['page' => 1]); +``` + +The `pushOntoQuery` method may be used to push additional parameters onto a query string parameter that has an array value: + +```php +$uri = $uri->pushOntoQuery('filter', ['active', 'pending']); +``` + +The `withoutQuery` method may be used to remove parameters from the query string: + +```php +$uri = $uri->withoutQuery(['page']); +``` + + +#### Generating Responses From URIs + +The `redirect` method may be used to generate a `RedirectResponse` instance to the given URI: + +```php +$uri = Uri::of('https://example.com'); + +return $uri->redirect(); +``` + +Or, you may simply return the `Uri` instance from a route or controller action, which will automatically generate a redirect response to the returned URI: + +```php +use Illuminate\Support\Facades\Route; +use Illuminate\Support\Uri; + +Route::get('/redirect', function () { + return Uri::to('/index') + ->withQuery(['sort' => 'name']); +}); +``` From 38c2a1afb1a2c865940f556d43caa31cd6b1bc23 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Apr 2025 15:41:56 -0500 Subject: [PATCH 053/162] wip --- helpers.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helpers.md b/helpers.md index 7e60698175..8ef04cb283 100644 --- a/helpers.md +++ b/helpers.md @@ -3283,6 +3283,9 @@ $uri = Uri::to('/dashboard'); $uri = Uri::route('user.profile', ['user' => 1]); $uri = Uri::action([UserController::class, 'index']); $uri = Uri::action(InvokableController::class); + +// Generate a URI instance from the current request URL... +$uri = $request->uri(); ``` Once you have a URI instance, you can fluently modify it: From 8576d22194f5f2cfc417526655298fc90fcaf61e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Apr 2025 15:43:06 -0500 Subject: [PATCH 054/162] wip --- helpers.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 8ef04cb283..860fb2c46d 100644 --- a/helpers.md +++ b/helpers.md @@ -3280,7 +3280,9 @@ $uri = Uri::of('https://example.com/path'); // Generate URI instances to paths, named routes, or controller actions... $uri = Uri::to('/dashboard'); -$uri = Uri::route('user.profile', ['user' => 1]); +$uri = Uri::route('users.show', ['user' => 1]); +$uri = Uri::signedRoute('users.show', ['user' => 1]); +$uri = Uri::temporarySignedRoute('user.index', now()->addMinutes(5)); $uri = Uri::action([UserController::class, 'index']); $uri = Uri::action(InvokableController::class); From 340d60f3057e63feca41115a2b9cd7808e83494e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 16 Apr 2025 06:56:42 -0500 Subject: [PATCH 055/162] wip --- database.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/database.md b/database.md index 40f5a06b17..14c9c68adb 100644 --- a/database.md +++ b/database.md @@ -379,20 +379,6 @@ DB::transaction(function () { }); ``` -You may optionally pass a closure to the `transaction` method that will be executed if the transaction fails: - -```php -use Illuminate\Support\Facades\DB; - -DB::transaction(function () { - DB::update('update users set votes = 1'); - - DB::delete('delete from posts'); -}, onFailure: function () { - // ... -}); -``` - #### Handling Deadlocks From 4fb3d916295f2584ac9a822305d91f017d51dd79 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 16 Apr 2025 10:36:07 -0500 Subject: [PATCH 056/162] document uri helper --- helpers.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/helpers.md b/helpers.md index 860fb2c46d..3afff368b8 100644 --- a/helpers.md +++ b/helpers.md @@ -144,6 +144,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [secure_asset](#method-secure-asset) [secure_url](#method-secure-url) [to_route](#method-to-route) +[uri](#method-uri) [url](#method-url)
@@ -1921,6 +1922,39 @@ If necessary, you may pass the HTTP status code that should be assigned to the r return to_route('users.show', ['user' => 1], 302, ['X-Framework' => 'Laravel']); ``` + +#### `uri()` {.collection-method} + +The `uri` function generates a [fluent URI instance](#uri) for the given URI: + +```php +$uri = uri('https://example.com') + ->withPath('/users') + ->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: + +```php +use App\Http\Controllers\UserController; + +$uri = uri([UserController::class, 'show'], ['user' => $user]) +``` + +If the controller is invokable, you may simply provide the controller class name: + +```php +use App\Http\Controllers\UserIndexController; + +$uri = uri(UserIndexController::class); +``` + +If the value given to the `uri` function matches the name of a [named route](/docs/{{version}}/routing#named-routes), a `Uri` instance will be generated for that route's path: + +```php +$uri = uri('users.show', ['user' => $user]); +``` + #### `url()` {.collection-method} From faa5d5cab30e0a82e9eaac67e5bb18f0715346c9 Mon Sep 17 00:00:00 2001 From: Dipesh Khanal <63183800+Dipesh79@users.noreply.github.com> Date: Fri, 18 Apr 2025 18:29:29 +0545 Subject: [PATCH 057/162] Adds Return Type to ```toSearchableArray``` method (#10332) --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index 81772e46a9..fe8bc33609 100644 --- a/scout.md +++ b/scout.md @@ -176,7 +176,7 @@ When utilizing Typesense, your searchable model's must define a `toSearchableArr * * @return array */ -public function toSearchableArray() +public function toSearchableArray(): array { return array_merge($this->toArray(),[ 'id' => (string) $this->id, From f25d00af1c9768dd409a4ae7d3c80a7409394de8 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:18:48 +0200 Subject: [PATCH 058/162] Use consistent Blade syntax highlighting for @use directive examples (#10335) --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index acf37a67f3..33d94d1275 100644 --- a/blade.md +++ b/blade.md @@ -648,7 +648,7 @@ Or, if you only need to use PHP to import a class, you may use the `@use` direct A second argument may be provided to the `@use` directive to alias the imported class: -```php +```blade @use('App\Models\Flight', 'FlightModel') ``` From 0adcedcf42721c9ea37ec17bd6fd31fb055b22f0 Mon Sep 17 00:00:00 2001 From: Achyut Neupane <30431426+achyutkneupane@users.noreply.github.com> Date: Mon, 21 Apr 2025 22:24:40 +0545 Subject: [PATCH 059/162] Added documentation for `spellOrdinal` in Number Facade (#10338) * Added documentation for `spellOrdinal` in Number Facade * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/helpers.md b/helpers.md index 3afff368b8..21efe7c662 100644 --- a/helpers.md +++ b/helpers.md @@ -108,6 +108,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::spellOrdinal](#method-number-spell-ordinal) [Number::trim](#method-number-trim) [Number::useLocale](#method-number-use-locale) [Number::withLocale](#method-number-with-locale) @@ -1649,6 +1650,27 @@ $number = Number::spell(10, until: 10); // 10 ``` + +#### `Number::spellOrdinal()` {.collection-method} + +The `Number::spellOrdinal` method returns the number's ordinal representation as a string of words: + +```php +use Illuminate\Support\Number; + +$number = Number::spellOrdinal(1); + +// first + +$number = Number::spellOrdinal(2); + +// second + +$number = Number::spellOrdinal(21); + +// twenty-first +``` + #### `Number::trim()` {.collection-method} From 0ca1cfe30a44f2a7074e9e45c062ffb24866667a Mon Sep 17 00:00:00 2001 From: Taner <224194+tanerkay@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:41:53 +0200 Subject: [PATCH 060/162] make it more clear what is happening with email RFC validation (#10340) --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 501f2cb7f0..064dce12c4 100644 --- a/validation.md +++ b/validation.md @@ -1506,8 +1506,8 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida
-- `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. +- `rfc`: `RFCValidation` - Validate the email address according to [supported RFCs](https://github.com/egulias/EmailValidator?tab=readme-ov-file#supported-rfcs). +- `strict`: `NoRFCWarningsValidation` - Validate the email according to [supported RFCs](https://github.com/egulias/EmailValidator?tab=readme-ov-file#supported-rfcs), failing when warnings are found (e.g. trailing periods and 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. From 6fdbe31a69ad4ec370984ad1fb92a1cd60a2ecca Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:47:23 +0200 Subject: [PATCH 061/162] Correct a small formatting issue (#10339) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 21efe7c662..94a2f8e845 100644 --- a/helpers.md +++ b/helpers.md @@ -1653,7 +1653,7 @@ $number = Number::spell(10, until: 10); #### `Number::spellOrdinal()` {.collection-method} -The `Number::spellOrdinal` method returns the number's ordinal representation as a string of words: +The `Number::spellOrdinal` method returns the number's ordinal representation as a string of words: ```php use Illuminate\Support\Number; From 45d6ce944a0e3635439149d3cee2daea1a37386c Mon Sep 17 00:00:00 2001 From: osama sadah <74357061+osama-98@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:50:56 +0300 Subject: [PATCH 062/162] feat: [12.x] Prohibited If Declined and Prohibited If Accepted validation rules (#10337) --- validation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/validation.md b/validation.md index 064dce12c4..23e6f2d2a5 100644 --- a/validation.md +++ b/validation.md @@ -1117,6 +1117,8 @@ Below is a list of all available validation rules and their function: [Present With All](#rule-present-with-all) [Prohibited](#rule-prohibited) [Prohibited If](#rule-prohibited-if) +[Prohibited If Accepted](#rule-prohibited-if-accepted) +[Prohibited If Declined](#rule-prohibited-if-declined) [Prohibited Unless](#rule-prohibited-unless) [Prohibits](#rule-prohibits) [Required](#rule-required) @@ -1996,6 +1998,15 @@ Validator::make($request->all(), [ 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin), ]); ``` + +#### prohibited_if_accepted:_anotherfield_,... + +The field under validation must be missing or empty if the _anotherfield_ field is equal to `"yes"`, `"on"`, `1`, `"1"`, `true`, or `"true"`. + + +#### prohibited_if_declined:_anotherfield_,... + +The field under validation must be missing or empty if the _anotherfield_ field is equal to `"no"`, `"off"`, `0`, `"0"`, `false`, or `"false"`. #### prohibited_unless:_anotherfield_,_value_,... From 4d09ace14696152c74ea55dc0094b179d3ea7326 Mon Sep 17 00:00:00 2001 From: Ostap Brehin Date: Tue, 22 Apr 2025 13:52:18 +0000 Subject: [PATCH 063/162] [12.x] Group imports in Blade `@use` directive (#10333) * Group imports in Blade `@use` directive * 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 33d94d1275..2cf02ae020 100644 --- a/blade.md +++ b/blade.md @@ -652,6 +652,12 @@ A second argument may be provided to the `@use` directive to alias the imported @use('App\Models\Flight', 'FlightModel') ``` +If you have multiple classes within the same namespace, you may group the imports of those classes: + +```blade +@use('App\Models\{Flight, Airport}') +``` + ### Comments From 9ec5d7e87062ae56400507ad99b133596139d4d3 Mon Sep 17 00:00:00 2001 From: Italo Date: Tue, 22 Apr 2025 10:09:52 -0400 Subject: [PATCH 064/162] Adds `AsColection::map()` section (#10327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds `AsColection::map()` section As part of [#55383](https://github.com/laravel/framework/pull/55383). * Added more clarification * More clarification * Update eloquent-mutators.md Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> * Adds openinh PHP on full class * Better, almost final, clarification. * Minor change to the Option example so it makes sense * Better clarification about serialization * formatting --------- Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 7aad935399..f0bd9c8d67 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -449,6 +449,74 @@ protected function casts(): array } ``` +The `of` method may be used to indicate collection items should be mapped into a given class via the collection's [`mapInto` method](/docs/{{version}}/collections#method-mapinto): + +```php +use App\ValueObjects\Option; +use Illuminate\Database\Eloquent\Casts\AsCollection; + +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'options' => AsCollection::of(Option::class) + ]; +} +``` + +When mapping collections to objects, the object should implement the `Illuminate\Contracts\Support\Arrayable` and `JsonSerializable` interfaces to define how their instances should be serialized into the database as JSON: + +```php + $this->name, + 'value' => $this->value, + 'is_locked' => $this->isLocked, + ]; + } + + /** + * Specify the data which should be serialized to JSON. + * + * @return array{name: string, data: string, is_locked: bool} + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } +} +``` + ### Date Casting From 16155241be01643c21d04f7971ac61c3e8ee8a44 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 23 Apr 2025 11:45:17 -0500 Subject: [PATCH 065/162] [12.x] clarify how to "set" value objects with custom casts (#10342) * clarify how to "set" value objects with custom casts the current wording only accounts for 1 of the 2 scenarios that can occur when using a custom cast with a value object. that Value Object can encompasses either 1 or many values on the model. the current documentation addresses the example of an `Address`, which would have multiple fields like "line1", "line2", "city", etc. this `set` method should return the array as currently documented. an example of a single value Value Object would be a phone number. a user might want it as an value object rather than a raw number to encapsulate behavior. this `set` method should return a string. there is one distinct advantage to returning a string vs an array. it prevents the cast from being tied to explicit model field names. this allows a field of any name to use the cast: ```php 'phone' => AsPhone::class, 'telephone' => AsPhone::class, ``` if a user returns an array from this simple 1 value cast, the cast loses its flexibility and the above example does not work. * 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 f0bd9c8d67..48b025d496 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -736,7 +736,7 @@ class User extends Model ### Value Object Casting -You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, the `set` method should return an array of key / value pairs that will be used to set raw, storable values on the model. +You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, if your value object encompasses more than one database column, the `set` method must return an array of key / value pairs that will be used to set raw, storable values on the model. If your value object only affects a single column, you should simply return the storable value. 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`: From 0bcc79f45778a01dd532c0cb7023bd0cc35fa316 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 23 Apr 2025 12:01:18 -0500 Subject: [PATCH 066/162] prefix casts with "As" (#10343) while this is definitely a subjective change, I think it provides some significant benefits: - consistent naming pattern with Laravel internal casts - prefix helps indicate a cast object, and avoid confusion with names of value objects - avoids having to alias imports. value objects get the noun, and casts get the prefix. generally speaking, I think these changes improve readability and consistency. --- eloquent-mutators.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 48b025d496..86132c3651 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -669,7 +669,7 @@ $users = User::select([ Laravel has a variety of built-in, helpful cast types; however, you may occasionally need to define your own cast types. To create a cast, execute the `make:cast` Artisan command. The new cast class will be placed in your `app/Casts` directory: ```shell -php artisan make:cast Json +php artisan make:cast AsJson ``` 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: @@ -682,7 +682,7 @@ namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Database\Eloquent\Model; -class Json implements CastsAttributes +class AsJson implements CastsAttributes { /** * Cast the given value. @@ -714,7 +714,7 @@ Once you have defined a custom cast type, you may attach it to a model attribute namespace App\Models; -use App\Casts\Json; +use App\Casts\AsJson; use Illuminate\Database\Eloquent\Model; class User extends Model @@ -727,7 +727,7 @@ class User extends Model protected function casts(): array { return [ - 'options' => Json::class, + 'options' => AsJson::class, ]; } } @@ -738,28 +738,28 @@ class User extends Model You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, if your value object encompasses more than one database column, the `set` method must return an array of key / value pairs that will be used to set raw, storable values on the model. If your value object only affects a single column, you should simply return the storable value. -As an example, we will define a custom cast class that casts multiple model values into a single `Address` value object. We will assume the `Address` value has two public properties: `lineOne` and `lineTwo`: +As an example, we will define a custom cast class that casts multiple model values into a single `Address` value object. We will assume the `Address` value object has two public properties: `lineOne` and `lineTwo`: ```php $attributes */ - public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject + public function get(Model $model, string $key, mixed $value, array $attributes): Address { - return new AddressValueObject( + return new Address( $attributes['address_line_one'], $attributes['address_line_two'] ); @@ -773,7 +773,7 @@ class Address implements CastsAttributes */ public function set(Model $model, string $key, mixed $value, array $attributes): array { - if (! $value instanceof AddressValueObject) { + if (! $value instanceof Address) { throw new InvalidArgumentException('The given value is not an Address instance.'); } @@ -808,7 +808,7 @@ When attributes that are cast to value objects are resolved, they are cached by 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 +class AsAddress implements CastsAttributes { public bool $withoutObjectCaching = true; @@ -843,7 +843,7 @@ Occasionally, you may need to write a custom cast class that only transforms val 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 +php artisan make:cast AsHash --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: @@ -856,7 +856,7 @@ namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes; use Illuminate\Database\Eloquent\Model; -class Hash implements CastsInboundAttributes +class AsHash implements CastsInboundAttributes { /** * Create a new cast class instance. @@ -893,7 +893,7 @@ When attaching a custom cast to a model, cast parameters may be specified by sep protected function casts(): array { return [ - 'secret' => Hash::class.':sha256', + 'secret' => AsHash::class.':sha256', ]; } ``` @@ -922,7 +922,7 @@ Objects that implement the `Castable` interface must define a `castUsing` method namespace App\ValueObjects; use Illuminate\Contracts\Database\Eloquent\Castable; -use App\Casts\Address as AddressCast; +use App\Casts\AsAddress; class Address implements Castable { @@ -933,7 +933,7 @@ class Address implements Castable */ public static function castUsing(array $arguments): string { - return AddressCast::class; + return AsAddress::class; } } ``` From 509ebaace5b004be4ce1b0ab16b1a4fa1892e866 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 23 Apr 2025 12:24:49 -0500 Subject: [PATCH 067/162] add fluent uri docs to url page --- urls.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/urls.md b/urls.md index 765c839e73..3be122ecaa 100644 --- a/urls.md +++ b/urls.md @@ -7,6 +7,7 @@ - [URLs for Named Routes](#urls-for-named-routes) - [Signed URLs](#signed-urls) - [URLs for Controller Actions](#urls-for-controller-actions) +- [Fluent URI Objects](#fluent-uri-objects) - [Default Values](#default-values) @@ -239,6 +240,47 @@ If the controller method accepts route parameters, you may pass an associative a $url = action([UserController::class, 'profile'], ['id' => 1]); ``` + +## Fluent URI Objects + +Laravel's `Uri` class provides a convenient and fluent interface for creating and manipulating URIs via objects. This class wraps the functionality provided by the underlying League URI package and integrates seamlessly with Laravel's routing system. + +You can create a `Uri` instance easily using static methods: + +```php +use App\Http\Controllers\UserController; +use App\Http\Controllers\InvokableController; +use Illuminate\Support\Uri; + +// Generate a URI instance from the given string... +$uri = Uri::of('https://example.com/path'); + +// Generate URI instances to paths, named routes, or controller actions... +$uri = Uri::to('/dashboard'); +$uri = Uri::route('users.show', ['user' => 1]); +$uri = Uri::signedRoute('users.show', ['user' => 1]); +$uri = Uri::temporarySignedRoute('user.index', now()->addMinutes(5)); +$uri = Uri::action([UserController::class, 'index']); +$uri = Uri::action(InvokableController::class); + +// Generate a URI instance from the current request URL... +$uri = $request->uri(); +``` + +Once you have a URI instance, you can fluently modify it: + +```php +$uri = Uri::of('https://example.com') + ->withScheme('http') + ->withHost('test.com') + ->withPort(8000) + ->withPath('/users') + ->withQuery(['page' => 2]) + ->withFragment('section-1'); +``` + +For more information on working with fluent URI objects, consult the [URI documentation](/docs/{{version}}/helpers#uris). + ## Default Values From 67a890f39eb9933044224ad64391e0be2cc692a5 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 23 Apr 2025 16:07:15 -0500 Subject: [PATCH 068/162] use multiline parameters for casting methods (#10345) I find the side scrolling for code snippets to decrease readability. these lengthy method signatures are often the culprit on this page. switching to multiline parameters to help avoid the necessity to side scroll. followed the PSR-12 recommendation for when dealing with multiline parameters AND a return type. --- eloquent-mutators.md | 64 +++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 86132c3651..b815e4643a 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -690,8 +690,12 @@ class AsJson implements CastsAttributes * @param array $attributes * @return array */ - public function get(Model $model, string $key, mixed $value, array $attributes): array - { + public function get( + Model $model, + string $key, + mixed $value, + array $attributes, + ): array { return json_decode($value, true); } @@ -700,8 +704,12 @@ class AsJson implements CastsAttributes * * @param array $attributes */ - public function set(Model $model, string $key, mixed $value, array $attributes): string - { + public function set( + Model $model, + string $key, + mixed $value, + array $attributes, + ): string { return json_encode($value); } } @@ -757,8 +765,12 @@ class AsAddress implements CastsAttributes * * @param array $attributes */ - 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'] @@ -771,8 +783,12 @@ class AsAddress implements CastsAttributes * @param array $attributes * @return array */ - public function set(Model $model, string $key, mixed $value, array $attributes): array - { + public function set( + Model $model, + string $key, + mixed $value, + array $attributes, + ): array { if (! $value instanceof Address) { throw new InvalidArgumentException('The given value is not an Address instance.'); } @@ -829,8 +845,12 @@ Therefore, you may specify that your custom cast class will be responsible for s * * @param array $attributes */ -public function serialize(Model $model, string $key, mixed $value, array $attributes): string -{ +public function serialize( + Model $model, + string $key, + mixed $value, + array $attributes, +): string { return (string) $value; } ``` @@ -870,8 +890,12 @@ class AsHash implements CastsInboundAttributes * * @param array $attributes */ - public function set(Model $model, string $key, mixed $value, array $attributes): string - { + public function set( + Model $model, + string $key, + mixed $value, + array $attributes, + ): string { return is_null($this->algorithm) ? bcrypt($value) : hash($this->algorithm, $value); @@ -977,16 +1001,24 @@ class Address implements Castable { 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 - { + public function set( + Model $model, + string $key, + mixed $value, + array $attributes, + ): array { return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, From a6d36e8d9e83a855cabea40713021d6b246c974c Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 23 Apr 2025 17:00:43 -0500 Subject: [PATCH 069/162] [12.x] document change in Eloquent Collection partition behavior (#10344) * document change in Eloquent Collection partition behavior document the code change in https://github.com/laravel/framework/pull/53304 * Update collections.md * Update eloquent-collections.md --------- Co-authored-by: Taylor Otwell --- collections.md | 3 +++ eloquent-collections.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/collections.md b/collections.md index 102c85b363..142be50028 100644 --- a/collections.md +++ b/collections.md @@ -2095,6 +2095,9 @@ $equalOrAboveThree->all(); // [3, 4, 5, 6] ``` +> [!NOTE] +> This method's behavior is modified when interacting with [Eloquent collections](/docs/{{version}}/eloquent-collections#method-partition). + #### `percentage()` {.collection-method} diff --git a/eloquent-collections.md b/eloquent-collections.md index 4790f7a04e..0a5b90bd29 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -80,6 +80,7 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe [makeVisible](#method-makeVisible) [makeHidden](#method-makeHidden) [only](#method-only) +[partition](#method-partition) [setVisible](#method-setVisible) [setHidden](#method-setHidden) [toQuery](#method-toquery) @@ -237,6 +238,19 @@ The `only` method returns all of the models that have the given primary keys: $users = $users->only([1, 2, 3]); ``` + +#### `partition` {.collection-method} + +The `partition` method returns an instance of `Illuminate\Support\Collection` containing `Illuminate\Database\Eloquent\Collection` collection instances: + +```php +$partition = $users->partition(fn ($user) => $user->age > 18); + +dump($partition::class); // Illuminate\Support\Collection +dump($partition[0]::class); // Illuminate\Database\Eloquent\Collection +dump($partition[1]::class); // Illuminate\Database\Eloquent\Collection +``` + #### `setVisible($attributes)` {.collection-method} From e128e9d1d74a55fd4fa9f989858824fc10dab3d0 Mon Sep 17 00:00:00 2001 From: AJ <60591772+devajmeireles@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:58:33 -0300 Subject: [PATCH 070/162] Closing missing php tag (#10347) --- billing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/billing.md b/billing.md index e9a1cf243b..e6f3ec7a8d 100644 --- a/billing.md +++ b/billing.md @@ -2211,6 +2211,7 @@ The `downloadInvoice` method also allows for a custom filename via its third arg ```php return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice'); +``` #### Custom Invoice Renderer From 294f7d8984091c25bb029752b98298f47cb9738b Mon Sep 17 00:00:00 2001 From: Pradeep Singh Date: Thu, 24 Apr 2025 19:34:44 +0530 Subject: [PATCH 071/162] Update notifications.md (#10346) --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 33854ff559..33066b16b5 100644 --- a/notifications.md +++ b/notifications.md @@ -1259,7 +1259,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. 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. +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: From b3927f93ba445e6b3040f2697c8611dd5918a655 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 24 Apr 2025 21:45:38 +0200 Subject: [PATCH 072/162] Correct the URI documentation link (#10348) --- urls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/urls.md b/urls.md index 3be122ecaa..93872c77fd 100644 --- a/urls.md +++ b/urls.md @@ -279,7 +279,7 @@ $uri = Uri::of('https://example.com') ->withFragment('section-1'); ``` -For more information on working with fluent URI objects, consult the [URI documentation](/docs/{{version}}/helpers#uris). +For more information on working with fluent URI objects, consult the [URI documentation](/docs/{{version}}/helpers#uri). ## Default Values From da16b495754d7c62da49f134c0470f0a165be17d Mon Sep 17 00:00:00 2001 From: David Heremans Date: Fri, 25 Apr 2025 17:06:30 +0200 Subject: [PATCH 073/162] Documents changes in https://github.com/laravel/sanctum/pull/564 (#10350) * Documents changes in https://github.com/laravel/sanctum/pull/564 Adds documentation to the Sanctum helper functions. * Update sanctum.md --------- Co-authored-by: Taylor Otwell --- sanctum.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sanctum.md b/sanctum.md index 5dfddf7038..ceb0cf3eba 100644 --- a/sanctum.md +++ b/sanctum.md @@ -280,6 +280,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. +To assist you in setting up your first-party stateful domains, Sanctum provides two helper functions that you can include in the configuration. First, `Sanctum::currentApplicationUrlWithPort()` will return the current application URL from the `APP_URL` environment variable, and `Sanctum::currentRequestHost()` will inject a placeholder into the stateful domain list which, at runtime, will be replaced by the host from the current request so that all requests with the same domain are considered stateful. + > [!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. From 014ef3e4c2f6d44c1c640f672205afc255442627 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:10:36 +0300 Subject: [PATCH 074/162] [12.x] Documenting the Uri pathSegments() method (#10349) * Documenting the Uri pathSegments() method * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/helpers.md b/helpers.md index 94a2f8e845..ca74b44d3e 100644 --- a/helpers.md +++ b/helpers.md @@ -3368,6 +3368,7 @@ $scheme = $uri->scheme(); $host = $uri->host(); $port = $uri->port(); $path = $uri->path(); +$segments = $uri->pathSegments(); $query = $uri->query(); $fragment = $uri->fragment(); ``` From f9f6b6f85812886208efaa6bc56632cb9e79babe Mon Sep 17 00:00:00 2001 From: Simon Paterson Date: Tue, 29 Apr 2025 03:56:16 +1000 Subject: [PATCH 075/162] Grammatical fix in scout.md (#10356) Co-authored-by: Simon Paterson --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index fe8bc33609..ee586db2b1 100644 --- a/scout.md +++ b/scout.md @@ -168,7 +168,7 @@ Additional settings and schema definitions for your Typesense collections can be #### 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: +When utilizing Typesense, your searchable models must define a `toSearchableArray` method that casts your model's primary key to a string and creation date to a UNIX timestamp: ```php /** From 16bb3a1db13b63cd6e8894b8e8f08faac89c2b06 Mon Sep 17 00:00:00 2001 From: KentarouTakeda Date: Tue, 29 Apr 2025 03:00:23 +0900 Subject: [PATCH 076/162] Add default connect timeout (10 seconds) to HTTP client documentation. (#10353) --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 42fe489d24..144b4458e8 100644 --- a/http-client.md +++ b/http-client.md @@ -251,7 +251,7 @@ $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: +You may specify the maximum number of seconds to wait while trying to connect to a server using the `connectTimeout` method. The default is 10 seconds: ```php $response = Http::connectTimeout(3)->get(/* ... */); From 13a0af1f377264b16183c87e80f29383a5f626d1 Mon Sep 17 00:00:00 2001 From: Morten Scheel Date: Tue, 29 Apr 2025 16:11:02 +0200 Subject: [PATCH 077/162] Fix invalid useCache() example (#10359) --- scheduling.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scheduling.md b/scheduling.md index d938d24cd0..c024d72dd7 100644 --- a/scheduling.md +++ b/scheduling.md @@ -365,10 +365,7 @@ Schedule::command('report:generate') You may use the `useCache` method to customize the cache store used by the scheduler to obtain the atomic locks necessary for single-server tasks: ```php -Schedule::command('recipes:sync') - ->everyThirtyMinutes() - ->onOneServer(); - ->useCache('database'); +Schedule::useCache('database'); ``` From ce64f20909c73edde0b06f0adf7dd5e61846d4a5 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 29 Apr 2025 18:09:36 +0300 Subject: [PATCH 078/162] Add missing namespace declaration to event example (#10351) --- broadcasting.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index 33460337fa..b8558cdb76 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -571,6 +571,8 @@ If you would like to broadcast your event using the `sync` queue instead of the ```php Date: Tue, 29 Apr 2025 22:20:01 +0300 Subject: [PATCH 079/162] Fix typo in interface name (#10360) --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index b815e4643a..abc982650b 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -476,7 +476,7 @@ When mapping collections to objects, the object should implement the `Illuminate namespace App\ValueObjects; use Illuminate\Contracts\Support\Arrayable; -use JsonSerilizable; +use JsonSerializable; class Option implements Arrayable, JsonSerializable { From b81e4857b27319231dd027ba949a7a5ca6f87833 Mon Sep 17 00:00:00 2001 From: Anthony Tibbs Date: Tue, 29 Apr 2025 16:48:35 -0400 Subject: [PATCH 080/162] [12.x] Typed getters for Arr helper (docs) (#10354) * add docs for typed Arr getters * add disambiguation to new HTML anchors (avoid potential future confusion) * add default value examples * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/helpers.md b/helpers.md index ca74b44d3e..254f3f17f3 100644 --- a/helpers.md +++ b/helpers.md @@ -40,6 +40,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::accessible](#method-array-accessible) [Arr::add](#method-array-add) +[Arr::array](#method-array-array) +[Arr::boolean](#method-array-boolean) [Arr::collapse](#method-array-collapse) [Arr::crossJoin](#method-array-crossjoin) [Arr::divide](#method-array-divide) @@ -48,10 +50,12 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::exists](#method-array-exists) [Arr::first](#method-array-first) [Arr::flatten](#method-array-flatten) +[Arr::float](#method-array-float) [Arr::forget](#method-array-forget) [Arr::get](#method-array-get) [Arr::has](#method-array-has) [Arr::hasAny](#method-array-hasany) +[Arr::integer](#method-array-integer) [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) [Arr::join](#method-array-join) @@ -76,6 +80,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::string](#method-array-string) [Arr::take](#method-array-take) [Arr::toCssClasses](#method-array-to-css-classes) [Arr::toCssStyles](#method-array-to-css-styles) @@ -261,6 +266,45 @@ $array = Arr::add(['name' => 'Desk', 'price' => null], 'price', 100); // ['name' => 'Desk', 'price' => 100] ``` + +#### `Arr::array()` {.collection-method} + +The `Arr::array` method retrieves a value from a deeply nested array using "dot" notation (just as [Arr::get()](#method-array-get) does), but throws an `InvalidArgumentException` if the requested value is not an `array`: + +``` +use Illuminate\Support\Arr; + +$array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; + +$value = Arr::array($array, 'languages'); + +// ['PHP', 'Ruby'] + +$value = Arr::array($array, 'name'); + +// throws InvalidArgumentException +``` + + +#### `Arr::boolean()` {.collection-method} + +The `Arr::boolean` method retrieves a value from a deeply nested array using "dot" notation (just as [Arr::get()](#method-array-get) does), but throws an `InvalidArgumentException` if the requested value is not a `boolean`: + +``` +use Illuminate\Support\Arr; + +$array = ['name' => 'Joe', 'available' => true]; + +$value = Arr::boolean($array, 'available'); + +// true + +$value = Arr::boolean($array, 'name'); + +// throws InvalidArgumentException +``` + + #### `Arr::collapse()` {.collection-method} @@ -413,6 +457,25 @@ $flattened = Arr::flatten($array); // ['Joe', 'PHP', 'Ruby'] ``` + +#### `Arr::float()` {.collection-method} + +The `Arr::float` method retrieves a value from a deeply nested array using "dot" notation (just as [Arr::get()](#method-array-get) does), but throws an `InvalidArgumentException` if the requested value is not a `float`: + +``` +use Illuminate\Support\Arr; + +$array = ['name' => 'Joe', 'balance' => 123.45]; + +$value = Arr::float($array, 'balance'); + +// 123.45 + +$value = Arr::float($array, 'name'); + +// throws InvalidArgumentException +``` + #### `Arr::forget()` {.collection-method} @@ -495,6 +558,25 @@ $contains = Arr::hasAny($array, ['category', 'product.discount']); // false ``` + +#### `Arr::integer()` {.collection-method} + +The `Arr::integer` method retrieves a value from a deeply nested array using "dot" notation (just as [Arr::get()](#method-array-get) does), but throws an `InvalidArgumentException` if the requested value is not an `int`: + +``` +use Illuminate\Support\Arr; + +$array = ['name' => 'Joe', 'age' => 42]; + +$value = Arr::integer($array, 'age'); + +// 42 + +$value = Arr::integer($array, 'name'); + +// throws InvalidArgumentException +``` + #### `Arr::isAssoc()` {.collection-method} @@ -1048,6 +1130,25 @@ If you would like the results sorted in descending order, you may use the `Arr:: $sorted = Arr::sortRecursiveDesc($array); ``` + +#### `Arr::string()` {.collection-method} + +The `Arr::string` method retrieves a value from a deeply nested array using "dot" notation (just as [Arr::get()](#method-array-get) does), but throws an `InvalidArgumentException` if the requested value is not a `string`: + +``` +use Illuminate\Support\Arr; + +$array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; + +$value = Arr::string($array, 'name'); + +// Joe + +$value = Arr::string($array, 'languages'); + +// throws InvalidArgumentException +``` + #### `Arr::take()` {.collection-method} From 019e51acc5a3b25f4d6a08a893d3c35c190ef079 Mon Sep 17 00:00:00 2001 From: Ro Date: Tue, 29 Apr 2025 14:50:45 -0600 Subject: [PATCH 081/162] [12.x] Add documentation on `@use` directive modifiers (#10361) * add documentation on `@use` directive modifiers * Update blade.md --------- Co-authored-by: Taylor Otwell --- blade.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/blade.md b/blade.md index 2cf02ae020..a483b2abe8 100644 --- a/blade.md +++ b/blade.md @@ -658,6 +658,26 @@ If you have multiple classes within the same namespace, you may group the import @use('App\Models\{Flight, Airport}') ``` +The `@use` directive also supports importing PHP functions and constants by prefixing the import path with the `function` or `const` modifiers: + +```blade +@use(function App\Helpers\format_currency) +``` + +Just like class imports, aliases are supported for functions and constants as well: + +```blade +@use(function App\Helpers\format_currency, 'formatMoney') +@use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES') +``` + +Grouped imports are also supported with both function and const modifiers, allowing you to import multiple symbols from the same namespace in a single directive: + +```blade +@use(function App\Helpers\{format_currency, format_date}) +@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT}) +``` + ### Comments From 7ee2ca445562457cc64f0a41637506c094138ba7 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:51:43 +0300 Subject: [PATCH 082/162] Fix missing text import (#10362) --- prompts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/prompts.md b/prompts.md index e0ef04fa0b..db60fb4b2e 100644 --- a/prompts.md +++ b/prompts.md @@ -812,6 +812,7 @@ If you need more granular control over a prompt in a form, you may invoke the `a ```php use function Laravel\Prompts\form; use function Laravel\Prompts\outro; +use function Laravel\Prompts\text; $responses = form() ->text('What is your name?', required: true, name: 'name') From b277950e07920ed147758944504688ec3d1adc0b Mon Sep 17 00:00:00 2001 From: Kim Hallberg Date: Wed, 30 Apr 2025 17:26:19 +0200 Subject: [PATCH 083/162] Add missing codeblock start (#10363) --- billing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/billing.md b/billing.md index e6f3ec7a8d..6412d34106 100644 --- a/billing.md +++ b/billing.md @@ -2218,6 +2218,7 @@ return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice'); Cashier also makes it possible to use a custom invoice renderer. By default, Cashier uses the `DompdfInvoiceRenderer` implementation, which utilizes the [dompdf](https://github.com/dompdf/dompdf) PHP library to generate Cashier's invoices. However, you may use any renderer you wish by implementing the `Laravel\Cashier\Contracts\InvoiceRenderer` interface. For example, you may wish to render an invoice PDF using an API call to a third-party PDF rendering service: +```php use Illuminate\Support\Facades\Http; use Laravel\Cashier\Contracts\InvoiceRenderer; use Laravel\Cashier\Invoice; From 41efa02e2d19610f141e4d7c89f5917965c79da3 Mon Sep 17 00:00:00 2001 From: Italo Date: Wed, 30 Apr 2025 17:09:43 -0400 Subject: [PATCH 084/162] Minor fix to the AsCollection Option example (#10365) To avoid confusion, the option constructor should receive an `array`, not each array member values. This is because `mapInto` and `map` will receive the item array completely. --- eloquent-mutators.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index abc982650b..340d9fd48a 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -480,15 +480,18 @@ use JsonSerializable; class Option implements Arrayable, JsonSerializable { + public string $name; + public mixed $value; + public bool $isLocked; + /** * Create a new Option instance. */ - public function __construct( - public string $name, - public mixed $value, - public bool $isLocked = false - ) { - // + public function __construct(array $data) + { + $this->name = $data['name']; + $this->value = $data['value']; + $this->isLocked = $data['is_locked']; } /** From 9600822e8d4dad2d054f2b99fd775b58c9537de1 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 1 May 2025 17:50:16 +0300 Subject: [PATCH 085/162] Fix link formatting (#10366) --- eloquent-mutators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 340d9fd48a..8f60617343 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -286,7 +286,7 @@ $user->mergeCasts([ #### Stringable Casting -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): +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): ```php Date: Thu, 1 May 2025 19:20:03 +0330 Subject: [PATCH 086/162] [12.x] Passport 13.x (#9819) * passport 13.x * wip * wip * wip * wip * wip * wip * wip * wip * wip * add device code grant * wip * wip * fix a typo on code verifier * rename CheckClientCredentials middleware * fix a typo * fix a typo * new `EnsureClientIsResourceOwner` middleware * wip * formatting * add axios link * wip * add sections for managing clients and tokens * formatting * better scope examples * better uri examples * formatting * formatting * add instructions for views * formatting * formatting * add samples for rendering views with closure * formatting * formatting --------- Co-authored-by: Taylor Otwell --- passport.md | 728 +++++++++++++++++++++++++++------------------------- 1 file changed, 384 insertions(+), 344 deletions(-) diff --git a/passport.md b/passport.md index 9c0fc9ece1..cb0c348418 100644 --- a/passport.md +++ b/passport.md @@ -6,30 +6,34 @@ - [Deploying Passport](#deploying-passport) - [Upgrading Passport](#upgrading-passport) - [Configuration](#configuration) - - [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) +- [Authorization Code Grant](#authorization-code-grant) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) + - [Managing Tokens](#managing-tokens) - [Refreshing Tokens](#refreshing-tokens) - [Revoking Tokens](#revoking-tokens) - [Purging Tokens](#purging-tokens) - [Authorization Code Grant With PKCE](#code-grant-pkce) - [Creating the Client](#creating-a-auth-pkce-grant-client) - [Requesting Tokens](#requesting-auth-pkce-grant-tokens) -- [Password Grant Tokens](#password-grant-tokens) +- [Device Authorization Grant](#device-authorization-grant) + - [Creating a Device Code Grant Client](#creating-a-device-authorization-grant-client) + - [Requesting Tokens](#requesting-device-authorization-grant-tokens) +- [Password Grant](#password-grant) - [Creating a Password Grant Client](#creating-a-password-grant-client) - [Requesting Tokens](#requesting-password-grant-tokens) - [Requesting All Scopes](#requesting-all-scopes) - [Customizing the User Provider](#customizing-the-user-provider) - [Customizing the Username Field](#customizing-the-username-field) - [Customizing the Password Validation](#customizing-the-password-validation) -- [Implicit Grant Tokens](#implicit-grant-tokens) -- [Client Credentials Grant Tokens](#client-credentials-grant-tokens) +- [Implicit Grant](#implicit-grant) +- [Client Credentials Grant](#client-credentials-grant) - [Personal Access Tokens](#personal-access-tokens) - [Creating a Personal Access Client](#creating-a-personal-access-client) + - [Customizing the User Provider](#customizing-the-user-provider-for-pat) - [Managing Personal Access Tokens](#managing-personal-access-tokens) - [Protecting Routes](#protecting-routes) - [Via Middleware](#via-middleware) @@ -39,7 +43,7 @@ - [Default Scope](#default-scope) - [Assigning Scopes to Tokens](#assigning-scopes-to-tokens) - [Checking Scopes](#checking-scopes) -- [Consuming Your API With JavaScript](#consuming-your-api-with-javascript) +- [SPA Authentication](#spa-authentication) - [Events](#events) - [Testing](#testing) @@ -48,7 +52,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] +> [!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. @@ -69,9 +73,7 @@ php artisan install:api --passport This command will publish and run the database migrations necessary for creating the tables your application needs to store OAuth2 clients and access tokens. The command will also create the encryption keys required to generate secure access tokens. -Additionally, this command will ask if you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers. - -After running the `install:api` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes: +After running the `install:api` command, add the `Laravel\Passport\HasApiTokens` trait and `Laravel\Passport\Contracts\OAuthenticatable` interface 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: ```php ## Configuration - -### 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\AppServiceProvider` class: - -```php -use Laravel\Passport\Passport; - -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. - ### 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\AppServiceProvider` class: ```php +use Carbon\CarbonInterval; + /** * Bootstrap any application services. */ public function boot(): void { - Passport::tokensExpireIn(now()->addDays(15)); - Passport::refreshTokensExpireIn(now()->addDays(30)); - Passport::personalAccessTokensExpireIn(now()->addMonths(6)); + Passport::tokensExpireIn(CarbonInterval::days(15)); + Passport::refreshTokensExpireIn(CarbonInterval::days(30)); + Passport::personalAccessTokensExpireIn(CarbonInterval::months(6)); } ``` @@ -207,7 +199,7 @@ After defining your model, you may instruct Passport to use your custom model vi ```php use App\Models\Passport\AuthCode; use App\Models\Passport\Client; -use App\Models\Passport\PersonalAccessClient; +use App\Models\Passport\DeviceCode; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; @@ -220,7 +212,7 @@ public function boot(): void Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); - Passport::usePersonalAccessClientModel(PersonalAccessClient::class); + Passport::useDeviceCodeModel(DeviceCode::class) } ``` @@ -241,7 +233,7 @@ public function register(): void } ``` -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: +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/master/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: ```php Route::group([ @@ -253,108 +245,85 @@ Route::group([ }); ``` - -## Issuing Access Tokens + +## Authorization Code Grant Using OAuth2 via authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client. - -### Managing Clients - -First, developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URL that your application can redirect to after users approve their request for authorization. - - -#### The `passport:client` Command +To get started, we need to instruct Passport how to return our "authorization" view. -The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create your own clients for testing your OAuth2 functionality. When you run the `client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: +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: -```shell -php artisan passport:client -``` - -**Redirect URLs** - -If you would like to allow multiple redirect URLs for your client, you may specify them using a comma-delimited list when prompted for the URL by the `passport:client` command. Any URLs which contain commas should be URL encoded: +```php +use Laravel\Passport\Passport; -```shell -http://example.com/callback,http://examplefoo.com/callback +/** + * Bootstrap any application services. + */ +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'], + ])); +} ``` - -#### JSON API - -Since your application's users will not be able to utilize the `client` command, Passport provides a JSON API that you may use to create clients. This saves you the trouble of having to manually code controllers for creating, updating, and deleting clients. +Passport will automatically define the `/oauth/authorize` route that returns this view. Your `auth.oauth.authorize` template should include a form that makes a POST request to the `passport.authorizations.approve` route to approve the authorization and a form that makes a DELETE request to the `passport.authorizations.deny` route to deny the authorization. The `passport.authorizations.approve` and `passport.authorizations.deny` routes expect `state`, `client_id`, and `auth_token` fields. -However, you will need to pair Passport's JSON API with your own frontend to provide a dashboard for your users to manage their clients. Below, we'll review all of the API endpoints for managing clients. For convenience, we'll use [Axios](https://github.com/axios/axios) to demonstrate making HTTP requests to the endpoints. + +### Managing Clients -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. +Developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URI that your application can redirect to after users approve their request for authorization. - -#### `GET /oauth/clients` + +#### First-Party Clients -This route returns all of the clients for the authenticated user. This is primarily useful for listing all of the user's clients so that they may edit or delete them: +The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create first-party clients or testing your OAuth2 functionality. When you run the `passport:client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: -```js -axios.get('/oauth/clients') - .then(response => { - console.log(response.data); - }); +```shell +php artisan passport:client ``` - -#### `POST /oauth/clients` - -This route is used to create new clients. It requires two pieces of data: the client's `name` and a `redirect` URL. The `redirect` URL is where the user will be redirected after approving or denying a request for authorization. +If you would like to allow multiple redirect URIs for your client, you may specify them using a comma-delimited list when prompted for the URI by the `passport:client` command. Any URIs which contain commas should be URI encoded: -When a client is created, it will be issued a client ID and client secret. These values will be used when requesting access tokens from your application. The client creation route will return the new client instance: - -```js -const data = { - name: 'Client Name', - redirect: 'http://example.com/callback' -}; - -axios.post('/oauth/clients', data) - .then(response => { - console.log(response.data); - }) - .catch (response => { - // List errors on response... - }); +```shell +https://third-party-app.com/callback,https://example.com/oauth/redirect ``` - -#### `PUT /oauth/clients/{client-id}` - -This route is used to update clients. It requires two pieces of data: the client's `name` and a `redirect` URL. The `redirect` URL is where the user will be redirected after approving or denying a request for authorization. The route will return the updated client instance: + +#### Third-Party Clients -```js -const data = { - name: 'New Client Name', - redirect: 'http://example.com/callback' -}; +Since your application's users will not be able to utilize the `passport:client` command, you may use `createAuthorizationCodeGrantClient` method of the `Laravel\Passport\ClientRepository` class to register a client for a given user: -axios.put('/oauth/clients/' + clientId, data) - .then(response => { - console.log(response.data); - }) - .catch (response => { - // List errors on response... - }); -``` +```php +use App\Models\User; +use Laravel\Passport\ClientRepository; - -#### `DELETE /oauth/clients/{client-id}` +$user = User::find($userId); -This route is used to delete clients: +// Creating an OAuth app client that belongs to the given user... +$client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( + user: $user, + name: 'Example App', + redirectUris: ['https://third-party-app.com/callback'], + confidential: false, + enableDeviceFlow: true +); -```js -axios.delete('/oauth/clients/' + clientId) - .then(response => { - // ... - }); +// Retrieving all the OAuth app clients that belong to the user... +$clients = $user->oauthApps()->get(); ``` +The `createAuthorizationCodeGrantClient` method returns an instance of `Laravel\Passport\Client`. You may display the `$client->id` as the client ID and `$client->plainSecret` as the client secret to the user. + ### Requesting Tokens @@ -371,15 +340,15 @@ 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', + 'client_id' => 'your-client-id', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', - 'scope' => '', + 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -397,12 +366,6 @@ If no `prompt` value is provided, the user will be prompted for authorization on 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: - -```shell -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: ```php @@ -410,14 +373,17 @@ Sometimes you may wish to skip the authorization prompt, such as when authorizin namespace App\Models\Passport; +use Illuminate\Contracts\Auth\Authenticatable; use Laravel\Passport\Client as BaseClient; class Client extends BaseClient { /** * Determine if the client should skip the authorization prompt. + * + * @param \Laravel\Passport\Scope[] $scopes */ - public function skipsAuthorization(): bool + public function skipsAuthorization(Authenticatable $user, array $scopes): bool { return $this->firstParty(); } @@ -442,11 +408,11 @@ Route::get('/callback', function (Request $request) { 'Invalid state value.' ); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + $response = Http::asForm()->post('https://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', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', + 'redirect_uri' => 'https://third-party-app.com/callback', 'code' => $request->code, ]); @@ -459,30 +425,35 @@ This `/oauth/token` route will return a JSON response containing `access_token`, > [!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. - -#### JSON API + +### Managing Tokens -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. +You may retrieve user's authorized tokens using the `tokens` method of the `Laravel\Passport\HasApiTokens` trait. For example, this may be used to offer your users a dashboard to keep track of their connections with third-party applications: - -#### `GET /oauth/tokens` - -This route returns all of the authorized access tokens that the authenticated user has created. This is primarily useful for listing all of the user's tokens so that they can revoke them: - -```js -axios.get('/oauth/tokens') - .then(response => { - console.log(response.data); - }); -``` - - -#### `DELETE /oauth/tokens/{token-id}` - -This route may be used to revoke authorized access tokens and their related refresh tokens: - -```js -axios.delete('/oauth/tokens/' + tokenId); +```php +use App\Models\User; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Facades\Date; +use Laravel\Passport\Token; + +$user = User::find($userId); + +// Retrieving all of the valid tokens for the user... +$tokens = $user->tokens() + ->where('revoked', false) + ->where('expires_at', '>', Date::now()) + ->get(); + +// Retrieving all the user's connections to third-party OAuth app clients... +$connections = $tokens->load('client') + ->reject(fn (Token $token) => $token->client->firstParty()) + ->groupBy('client_id') + ->map(fn (Collection $tokens) => [ + 'client' => $tokens->first()->client, + 'scopes' => $tokens->pluck('scopes')->flatten()->unique()->values()->all(), + 'tokens_count' => $tokens->count(), + ]) + ->values(); ``` @@ -493,12 +464,12 @@ If your application issues short-lived access tokens, users will need to refresh ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', - 'scope' => '', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // Required for confidential clients only... + 'scope' => 'user:read orders:create', ]); return $response->json(); @@ -509,20 +480,25 @@ This `/oauth/token` route will return a JSON response containing `access_token`, ### Revoking Tokens -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): +You may revoke a token by using the `revoke` method on the `Laravel\Passport\Token` model. You may revoke a token's refresh token using the `revoke` method on the `Laravel\Passport\RefreshToken` model: ```php -use Laravel\Passport\TokenRepository; -use Laravel\Passport\RefreshTokenRepository; +use Laravel\Passport\Passport; +use Laravel\Passport\Token; -$tokenRepository = app(TokenRepository::class); -$refreshTokenRepository = app(RefreshTokenRepository::class); +$token = Passport::token()->find($tokenId); // Revoke an access token... -$tokenRepository->revokeAccessToken($tokenId); +$token->revoke(); + +// Revoke the token's refresh token... +$token->refreshToken?->revoke(); -// Revoke all of the token's refresh tokens... -$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); +// Revoke all of the user's tokens... +User::find($userId)->tokens()->each(function (Token $token) { + $token->revoke(); + $token->refreshToken?->revoke(); +}); ``` @@ -531,16 +507,16 @@ $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); When tokens have been revoked or expired, you might want to purge them from the database. Passport's included `passport:purge` Artisan command can do this for you: ```shell -# Purge revoked and expired tokens and auth codes... +# Purge revoked and expired tokens, auth codes, and device 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... +# Only purge revoked tokens, auth codes, and device codes... php artisan passport:purge --revoked -# Only purge expired tokens and auth codes... +# Only purge expired tokens, auth codes, and device codes... php artisan passport:purge --expired ``` @@ -555,7 +531,7 @@ Schedule::command('passport:purge')->hourly(); ## Authorization Code Grant With PKCE -The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or native applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. +The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or mobile applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. ### Creating the Client @@ -579,7 +555,7 @@ 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. ```php -$encoded = base64_encode(hash('sha256', $code_verifier, true)); +$encoded = base64_encode(hash('sha256', $codeVerifier, true)); $codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_'); ``` @@ -597,25 +573,25 @@ Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $request->session()->put( - 'code_verifier', $code_verifier = Str::random(128) + 'code_verifier', $codeVerifier = Str::random(128) ); $codeChallenge = strtr(rtrim( - base64_encode(hash('sha256', $code_verifier, true)) + base64_encode(hash('sha256', $codeVerifier, true)) , '='), '+/', '-_'); $query = http_build_query([ - 'client_id' => 'client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'client_id' => 'your-client-id', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', - 'scope' => '', + 'scope' => 'user:read orders:create', '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('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -640,10 +616,10 @@ Route::get('/callback', function (Request $request) { InvalidArgumentException::class ); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', - 'client_id' => 'client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'client_id' => 'your-client-id', + 'redirect_uri' => 'https://third-party-app.com/callback', 'code_verifier' => $codeVerifier, 'code' => $request->code, ]); @@ -652,8 +628,132 @@ Route::get('/callback', function (Request $request) { }); ``` - -## Password Grant Tokens + +## Device Authorization Grant + +The OAuth2 device authorization grant allows browserless or limited input devices, such as TVs and game consoles, to obtain an access token by exchanging a "device code". When using device flow, the device client will instruct the user to use a secondary device, such as a computer or a smartphone and connect to your server where they will enter the provided "user code" and either approve or deny the access request. + +To get started, we need to instruct Passport how to return our "user code" and "authorization" views. + +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 Laravel\Passport\Passport; + +/** + * Bootstrap any application services. + */ +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')); + + 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 will automatically define routes that return these views. Your `auth.oauth.device.user-code` template should include a form that makes a GET request to the `passport.device.authorizations.authorize` route. The `passport.device.authorizations.authorize` route expects a `user_code` query parameter. + +Your `auth.oauth.device.authorize` template should include a form that makes a POST request to the `passport.device.authorizations.approve` route to approve the authorization and a form that makes a DELETE request to the `passport.device.authorizations.deny` route to deny the authorization. The `passport.device.authorizations.approve` and `passport.device.authorizations.deny` routes expect `state`, `client_id`, and `auth_token` fields. + + +### Creating a Device Authorization Grant Client + +Before your application can issue tokens via the device authorization grant, you will need to create a device flow enabled client. You may do this using the `passport:client` Artisan command with the `--device` option. This command will create a first-party device flow enabled client and provide you with a client ID and secret: + +```shell +php artisan passport:client --device +``` + +Additionally, you may use `createDeviceAuthorizationGrantClient` method on the `ClientRepository` class to register a third-party client that belongs to the given user: + +```php +use App\Models\User; +use Laravel\Passport\ClientRepository; + +$user = User::find($userId); + +$client = app(ClientRepository::class)->createDeviceAuthorizationGrantClient( + user: $user, + name: 'Example Device', + confidential: false, +); +``` + + +### Requesting Tokens + + +#### Requesting a Device Code + +Once a client has been created, developers may use their client ID to request a device code from your application. First, the consuming device should make a `POST` request to your application's `/oauth/device/code` route to request a device code: + +```php +use Illuminate\Support\Facades\Http; + +$response = Http::asForm()->post('https://passport-app.test/oauth/device/code', [ + 'client_id' => 'your-client-id', + 'scope' => 'user:read orders:create', +]); + +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] +> Remember, the `/oauth/device/code` route is already defined by Passport. You do not need to manually define this route. + + +#### Displaying the Verification URI and User Code + +Once a device code request has been obtained, the consuming device should instruct the user to use another device and visit the provided `verification_uri` and enter the `user_code` in order to approve the authorization request. + + +#### Polling Token Request + +Since the user will be using a separate device to grant (or deny) access, the consuming device should poll your application's `/oauth/token` route to determine when the user has responded to the request. The consuming device should use the minimum polling `interval` provided in the JSON response when requesting device code to avoid rate limit errors: + +```php +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Sleep; + +$interval = 5; + +do { + Sleep::for($interval)->seconds(); + + $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 + 'device_code' => 'the-device-code', + ]); + + if ($response->json('error') === 'slow_down') { + $interval += 5; + } +} while (in_array($response->json('error'), ['authorization_pending', 'slow_down'])); + +return $response->json(); +``` + +If the user has approved the authorization request, this 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. + + +## Password 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/). @@ -675,7 +775,7 @@ public function boot(): void ### Creating a Password Grant Client -Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` Artisan command with the `--password` option. **If you have already run the `passport:install` command, you do not need to run this command:** +Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` Artisan command with the `--password` option. ```shell php artisan passport:client --password @@ -684,18 +784,18 @@ 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 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: +Once you have enabled the grant and 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: ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', - 'scope' => '', + 'scope' => 'user:read orders:create', ]); return $response->json(); @@ -712,10 +812,10 @@ When using the password grant or client credentials grant, you may wish to autho ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '*', @@ -725,7 +825,7 @@ $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ ### Customizing the User Provider -If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized. +If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#multiple-authentication-guards) to ensure that only users from the guard's specified provider are authorized. ### Customizing the Username Field @@ -784,8 +884,8 @@ class User extends Authenticatable } ``` - -## Implicit Grant Tokens + +## Implicit 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/). @@ -802,7 +902,13 @@ public function boot(): void } ``` -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: +Before your application can issue tokens via the implicit grant, you will need to create an implicit grant client. You may do this using the `passport:client` Artisan command with the `--implicit` option. + +```shell +php artisan passport:client --implicit +``` + +Once the grant has been enabled and an implicit client has been created, 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: ```php use Illuminate\Http\Request; @@ -811,23 +917,23 @@ 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', + 'client_id' => 'your-client-id', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'token', - 'scope' => '', + 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://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. - -## Client Credentials Grant Tokens + +## Client Credentials Grant The client credentials grant is suitable for machine-to-machine authentication. For example, you might use this grant in a scheduled job which is performing maintenance tasks over an API. @@ -837,32 +943,22 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, to use this grant type, register a middleware alias for the `CheckClientCredentials` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: +Next, assign the `Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner` middleware to a route: ```php -use Laravel\Passport\Http\Middleware\CheckClientCredentials; +use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner; -->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'client' => CheckClientCredentials::class - ]); -}) -``` - -Then, attach the middleware to a route: - -```php Route::get('/orders', function (Request $request) { - // ... -})->middleware('client'); + // Access token is valid and the client is resource owner... +})->middleware(EnsureClientIsResourceOwner::class); ``` -To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route: +To restrict access to the route to specific scopes, you may provide a list of the required scopes to the `using` method`: ```php Route::get('/orders', function (Request $request) { - // ... -})->middleware('client:check-status,your-scope'); + // Access token is valid, the client is resource owner, and has both "servers:read" and "servers:create" scopes... +})->middleware(EnsureClientIsResourceOwner::using('servers:read', 'servers:create'); ``` @@ -873,11 +969,11 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'client_credentials', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', - 'scope' => 'your-scope', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', + 'scope' => 'servers:read servers:create', ]); return $response->json()['access_token']; @@ -889,7 +985,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] -> 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. +> If your application is using Passport primarily 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 @@ -900,12 +996,10 @@ Before your application can issue personal access tokens, you will need to creat php artisan passport:client --personal ``` -After creating your personal access client, place the client's ID and plain-text secret value in your application's `.env` file: + +### Customizing the User Provider -```ini -PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value" -PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value" -``` +If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the personal access grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --personal` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#multiple-authentication-guards) to ensure that only users from the guard's specified provider are authorized. ### Managing Personal Access Tokens @@ -914,74 +1008,27 @@ Once you have created a personal access client, you may issue tokens for a given ```php use App\Models\User; +use Illuminate\Support\Facades\Date; +use Laravel\Passport\Token; -$user = User::find(1); +$user = User::find($userId); // Creating a token without scopes... -$token = $user->createToken('Token Name')->accessToken; +$token = $user->createToken('My Token')->accessToken; // Creating a token with scopes... -$token = $user->createToken('My Token', ['place-orders'])->accessToken; -``` - - -#### 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/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. - - -#### `GET /oauth/scopes` - -This route returns all of the [scopes](#token-scopes) defined for your application. You may use this route to list the scopes a user may assign to a personal access token: - -```js -axios.get('/oauth/scopes') - .then(response => { - console.log(response.data); - }); -``` - - -#### `GET /oauth/personal-access-tokens` - -This route returns all of the personal access tokens that the authenticated user has created. This is primarily useful for listing all of the user's tokens so that they may edit or revoke them: - -```js -axios.get('/oauth/personal-access-tokens') - .then(response => { - console.log(response.data); - }); -``` - - -#### `POST /oauth/personal-access-tokens` - -This route creates new personal access tokens. It requires two pieces of data: the token's `name` and the `scopes` that should be assigned to the token: - -```js -const data = { - name: 'Token Name', - scopes: [] -}; +$token = $user->createToken('My Token', ['user:read', 'orders:create'])->accessToken; -axios.post('/oauth/personal-access-tokens', data) - .then(response => { - console.log(response.data.accessToken); - }) - .catch (response => { - // List errors on response... - }); -``` - - -#### `DELETE /oauth/personal-access-tokens/{token-id}` +// Creating a token with all scopes... +$token = $user->createToken('My Token', ['*'])->accessToken; -This route may be used to revoke personal access tokens: - -```js -axios.delete('/oauth/personal-access-tokens/' + tokenId); +// Retrieving all the valid personal access tokens that belong to the user... +$tokens = $user->tokens() + ->with('client') + ->where('revoked', false) + ->where('expires_at', '>', Date::now()) + ->get() + ->filter(fn (Token $token) => $token->client->hasGrantType('personal_access')); ``` @@ -994,12 +1041,12 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add ```php Route::get('/user', function () { - // ... + // Only API authenticated users may access this route... })->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. +> If you are using the [client credentials grant](#client-credentials-grant), you should use [the `Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner` middleware](#client-credentials-grant) to protect your routes instead of the `auth:api` middleware. #### Multiple Authentication Guards @@ -1007,14 +1054,16 @@ Route::get('/user', function () { 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: ```php -'api' => [ - 'driver' => 'passport', - 'provider' => 'users', -], +'guards' => [ + 'api' => [ + 'driver' => 'passport', + 'provider' => 'users', + ], -'api-customers' => [ - 'driver' => 'passport', - 'provider' => 'customers', + 'api-customers' => [ + 'driver' => 'passport', + 'provider' => 'customers', + ], ], ``` @@ -1027,19 +1076,19 @@ Route::get('/customer', function () { ``` > [!NOTE] -> For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). +> For more information on using multiple user providers with Passport, please consult the [personal access tokens documentation](#customizing-the-user-provider-for-pat) and [password grant documentation](#customizing-the-user-provider). ### Passing the Access Token -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: +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 `Http` Facade: ```php use Illuminate\Support\Facades\Http; $response = Http::withHeaders([ 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$accessToken, + 'Authorization' => "Bearer $accessToken", ])->get('https://passport-app.test/api/user'); return $response->json(); @@ -1062,8 +1111,9 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` public function boot(): void { Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', + 'user:read' => 'Retrieve the user info', + 'orders:create' => 'Place orders', + 'orders:read:status' => 'Check order status', ]); } ``` @@ -1071,25 +1121,23 @@ public function boot(): void ### 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: +If a client does not request any specific scopes, you may configure your Passport server to attach default scopes to the token using the `defaultScopes` method. Typically, you should call this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class: ```php use Laravel\Passport\Passport; Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', + 'user:read' => 'Retrieve the user info', + 'orders:create' => 'Place orders', + 'orders:read:status' => 'Check order status', ]); -Passport::setDefaultScope([ - 'check-status', - 'place-orders', +Passport::defaultScopes([ + 'user:read', + 'orders:create', ]); ``` -> [!NOTE] -> Passport's default scopes do not apply to personal access tokens that are generated by the user. - ### Assigning Scopes to Tokens @@ -1101,13 +1149,13 @@ When requesting an access token using the authorization code grant, consumers sh ```php Route::get('/redirect', function () { $query = http_build_query([ - 'client_id' => 'client-id', - 'redirect_uri' => 'http://example.com/callback', + 'client_id' => 'your-client-id', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', - 'scope' => 'place-orders check-status', + 'scope' => 'user:read orders:create', ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -1117,46 +1165,38 @@ Route::get('/redirect', function () { 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: ```php -$token = $user->createToken('My Token', ['place-orders'])->accessToken; +$token = $user->createToken('My Token', ['orders:create'])->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: - -```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, - ]); -}) -``` +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. #### Check For All Scopes -The `scopes` middleware may be assigned to a route to verify that the incoming request's access token has all of the listed scopes: +The `Laravel\Passport\Http\Middleware\CheckToken` middleware may be assigned to a route to verify that the incoming request's access token has all the listed scopes: ```php +use Laravel\Passport\Http\Middleware\CheckToken; + Route::get('/orders', function () { - // Access token has both "check-status" and "place-orders" scopes... -})->middleware(['auth:api', 'scopes:check-status,place-orders']); + // Access token has both "orders:read" and "orders:create" scopes... +})->middleware(['auth:api', CheckToken::using('orders:read', 'orders:create'); ``` #### 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: +The `Laravel\Passport\Http\Middleware\CheckTokenForAnyScope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: ```php +use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope; + Route::get('/orders', function () { - // Access token has either "check-status" or "place-orders" scope... -})->middleware(['auth:api', 'scope:check-status,place-orders']); + // Access token has either "orders:read" or "orders:create" scope... +})->middleware(['auth:api', CheckTokenForAnyScope::using('orders:read', 'orders:create'); ``` @@ -1168,7 +1208,7 @@ Once an access token authenticated request has entered your application, you may use Illuminate\Http\Request; Route::get('/orders', function (Request $request) { - if ($request->user()->tokenCan('place-orders')) { + if ($request->user()->tokenCan('orders:create')) { // ... } }); @@ -1194,17 +1234,17 @@ Passport::scopes(); The `scopesFor` method will return an array of `Laravel\Passport\Scope` instances matching the given IDs / names: ```php -Passport::scopesFor(['place-orders', 'check-status']); +Passport::scopesFor(['user:read', 'orders:create']); ``` You may determine if a given scope has been defined using the `hasScope` method: ```php -Passport::hasScope('place-orders'); +Passport::hasScope('orders:create'); ``` - -## Consuming Your API With JavaScript + +## SPA Authentication 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. @@ -1250,7 +1290,7 @@ public function boot(): void #### CSRF Protection -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. +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 included with the skeleton application and all starter kits includes an [Axios](https://github.com/axios/axios) instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. > [!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()`. @@ -1278,13 +1318,13 @@ Passport's `actingAs` method may be used to specify the currently authenticated use App\Models\User; use Laravel\Passport\Passport; -test('servers can be created', function () { +test('orders can be created', function () { Passport::actingAs( User::factory()->create(), - ['create-servers'] + ['orders:create'] ); - $response = $this->post('/api/create-server'); + $response = $this->post('/api/orders'); $response->assertStatus(201); }); @@ -1294,14 +1334,14 @@ test('servers can be created', function () { use App\Models\User; use Laravel\Passport\Passport; -public function test_servers_can_be_created(): void +public function test_orders_can_be_created(): void { Passport::actingAs( User::factory()->create(), - ['create-servers'] + ['orders:create'] ); - $response = $this->post('/api/create-server'); + $response = $this->post('/api/orders'); $response->assertStatus(201); } @@ -1313,13 +1353,13 @@ Passport's `actingAsClient` method may be used to specify the currently authenti use Laravel\Passport\Client; use Laravel\Passport\Passport; -test('orders can be retrieved', function () { +test('servers can be retrieved', function () { Passport::actingAsClient( Client::factory()->create(), - ['check-status'] + ['servers:read'] ); - $response = $this->get('/api/orders'); + $response = $this->get('/api/servers'); $response->assertStatus(200); }); @@ -1329,14 +1369,14 @@ test('orders can be retrieved', function () { use Laravel\Passport\Client; use Laravel\Passport\Passport; -public function test_orders_can_be_retrieved(): void +public function test_servers_can_be_retrieved(): void { Passport::actingAsClient( Client::factory()->create(), - ['check-status'] + ['servers:read'] ); - $response = $this->get('/api/orders'); + $response = $this->get('/api/servers'); $response->assertStatus(200); } From 8f2988ffbbf6b0299c2096d5687b71782863c01a Mon Sep 17 00:00:00 2001 From: Simon Paterson Date: Sat, 3 May 2025 00:49:14 +1000 Subject: [PATCH 087/162] Query builder first mention (#10368) * Grammatical fix in scout.md * Add `use` statement to first occurance of Query Builder * Add `use` statement to first occurrence of Query Builder --------- Co-authored-by: Simon Paterson --- queries.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/queries.md b/queries.md index f0d12effda..9a222cc6b7 100644 --- a/queries.md +++ b/queries.md @@ -568,6 +568,8 @@ $users = DB::table('users') If you need to group an "or" condition within parentheses, you may pass a closure as the first argument to the `orWhere` method: ```php +use Illuminate\Database\Query\Builder; + $users = DB::table('users') ->where('votes', '>', 100) ->orWhere(function (Builder $query) { From 9aa245980ae835bc246391e58515f92837511aef Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 2 May 2025 17:55:51 +0300 Subject: [PATCH 088/162] Fix Number::pairs example to reflect actual behavior (#10367) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 254f3f17f3..e8f8972daa 100644 --- a/helpers.md +++ b/helpers.md @@ -1678,7 +1678,7 @@ use Illuminate\Support\Number; $result = Number::pairs(25, 10); -// [[1, 10], [11, 20], [21, 25]] +// [[0, 9], [10, 19], [20, 25]] $result = Number::pairs(25, 10, offset: 0); From 1aed93e9be68080e9b933e3f83728d5ca5f022bf Mon Sep 17 00:00:00 2001 From: Sean McKenzie <70048908+seanmckenzie428@users.noreply.github.com> Date: Sun, 4 May 2025 19:35:02 -0600 Subject: [PATCH 089/162] Fix typo in validation.md (#10371) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 23e6f2d2a5..520ab8c703 100644 --- a/validation.md +++ b/validation.md @@ -2243,7 +2243,7 @@ You may specify additional query conditions by customizing the query using the ` 'email' => Rule::unique('users')->where(fn (Builder $query) => $query->where('account_id', 1)) ``` -**Ignoring Soft Deleteded Records in Unique Checks:** +**Ignoring Soft Deleted 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: From 78402ebd17f4292e973c2d4355b0d257582c57ca Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 5 May 2025 05:05:16 +0330 Subject: [PATCH 090/162] Update middleware.md (#10372) --- middleware.md | 1 - 1 file changed, 1 deletion(-) diff --git a/middleware.md b/middleware.md index 4b7139a539..cddd2a0945 100644 --- a/middleware.md +++ b/middleware.md @@ -427,7 +427,6 @@ class EnsureUserHasRole return $next($request); } - } ``` From 226ee95d7c38ab681259843c60b40e061b932d09 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 5 May 2025 04:45:24 +0300 Subject: [PATCH 091/162] Clarify collapse method behavior (#10370) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 142be50028..18e04d96bf 100644 --- a/collections.md +++ b/collections.md @@ -417,7 +417,7 @@ $chunks->all(); #### `collapse()` {.collection-method} -The `collapse` method collapses a collection of arrays into a single, flat collection: +The `collapse` method collapses a collection of arrays or collections into a single, flat collection: ```php $collection = collect([ From cc12f82e579b97571cfe1198e088bb92005682f8 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Wed, 7 May 2025 02:40:00 +0200 Subject: [PATCH 092/162] valet: add cloudflared option (#10375) * valet: add cloudflared option * Update valet.md --------- Co-authored-by: Taylor Otwell --- valet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/valet.md b/valet.md index 45839e43dd..cae1a895cb 100644 --- a/valet.md +++ b/valet.md @@ -276,13 +276,13 @@ valet unisolate 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. -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`: +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 `ngrok`, `expose`, or `cloudflared`: ```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. +If you choose a tool and don't have it installed via Homebrew (for ngrok and cloudflared) 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: From 14d1cfa4629514d4d5c35bcb76970bd3e2aaf9bd Mon Sep 17 00:00:00 2001 From: Roshandelpoor Mohammad Date: Wed, 7 May 2025 20:45:41 +0330 Subject: [PATCH 093/162] [12.x] for consistency, standardize on american English (#10376) * Update vite.md * Update queues.md --- queues.md | 2 +- vite.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index e98b257d54..a0e90207a3 100644 --- a/queues.md +++ b/queues.md @@ -705,7 +705,7 @@ public function middleware(): array } ``` -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`: +By default, this middleware will throttle every exception. You can modify this behavior 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`: ```php use Illuminate\Http\Client\HttpClientException; diff --git a/vite.md b/vite.md index 2611b84763..6e84c64b4c 100644 --- a/vite.md +++ b/vite.md @@ -1005,7 +1005,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 behavior, 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 1ecc78137050778770bc45d5bad395b73c0100df Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Wed, 7 May 2025 19:21:55 +0200 Subject: [PATCH 094/162] Add Exceptions FQN use (#10378) --- http-client.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http-client.md b/http-client.md index 144b4458e8..692392ea9a 100644 --- a/http-client.md +++ b/http-client.md @@ -398,6 +398,8 @@ return Http::post(/* ... */)->throw(function (Response $response, RequestExcepti 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: ```php +use Illuminate\Foundation\Configuration\Exceptions; + ->withExceptions(function (Exceptions $exceptions) { // Truncate request exception messages to 240 characters... $exceptions->truncateRequestExceptionsAt(240); From bcdcecaa5b21545b94de9d10d4e5518f1d8c2b90 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 10:32:41 -0700 Subject: [PATCH 095/162] add redirect back --- http-tests.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/http-tests.md b/http-tests.md index 70bffc4a2c..7a5dabbb86 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1003,6 +1003,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertPaymentRequired](#assert-payment-required) [assertPlainCookie](#assert-plain-cookie) [assertRedirect](#assert-redirect) +[assertRedirectBack](#assert-redirect-back) [assertRedirectContains](#assert-redirect-contains) [assertRedirectToRoute](#assert-redirect-to-route) [assertRedirectToSignedRoute](#assert-redirect-to-signed-route) @@ -1541,6 +1542,15 @@ Assert that the response is a redirect to the given URI: $response->assertRedirect($uri = null); ``` + +#### assertRedirectBack + +Assert whether the response is redirecting back to the previous page: + +```php +$response->assertRedirectBack(); +``` + #### assertRedirectContains From d988d32696737b69c01760c81d2963183f30321e Mon Sep 17 00:00:00 2001 From: Adam Griffith <5164766+adamjgriffith@users.noreply.github.com> Date: Wed, 7 May 2025 18:36:26 +0100 Subject: [PATCH 096/162] [12.x] Document releaseAfter method (#10379) * Document releaseAfter method * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/queues.md b/queues.md index a0e90207a3..b10b6f9d46 100644 --- a/queues.md +++ b/queues.md @@ -525,6 +525,20 @@ public function middleware(): array 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. +Using the `releaseAfter` method, you may also specify the number of seconds that must elapse before the released job will be attempted again: + +```php +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new RateLimited('backups'))->releaseAfter(60)]; +} +``` + If you do not want a job to be retried when it is rate limited, you may use the `dontRelease` method: ```php From 894cbdcca063efc370d3b16473ff58268f6cd6ef Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 10:40:19 -0700 Subject: [PATCH 097/162] name queued closures: --- queues.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/queues.md b/queues.md index b10b6f9d46..dfbb1dc5a5 100644 --- a/queues.md +++ b/queues.md @@ -1819,6 +1819,14 @@ dispatch(function () use ($podcast) { }); ``` +To assign a name to the queued closure which may be used by queue reporting dashboards, as well as be displayed by the `queue:work` command, you may use the `name` method: + +```php +dispatch(function () { + // ... +})->name('Publish Podcast'); +``` + 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): ```php From 88c3f8cb2c397968bdfe79dc12a0e146a3ef03b1 Mon Sep 17 00:00:00 2001 From: DeffiDeffi Date: Wed, 7 May 2025 20:02:54 +0200 Subject: [PATCH 098/162] [12.x] Add docs about using 'exists' rule on array (#10374) * add documentation for array of values on 'exists' * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/validation.md b/validation.md index 520ab8c703..f4f52e69e8 100644 --- a/validation.md +++ b/validation.md @@ -1677,6 +1677,17 @@ You may explicitly specify the database column name that should be used by the ` 'state' => Rule::exists('states', 'abbreviation'), ``` + +#### Validating an Array of Values + +Sometimes, you may wish to validate whether an array of values exists in the database. You can do so by adding both the `exists` and [array](#rule-array) rules to the field being validated: + +```php +'user_ids' => ['array', 'exists:users,id'], +``` + +When both of these rules are assigned to a field, Laravel will automatically build a single query to determine if all of the given values exist in the specified table. + #### extensions:_foo_,_bar_,... From 07ca01f1c6afa854057f10e9e1b2edbb374285fd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 11:04:05 -0700 Subject: [PATCH 099/162] formatting --- validation.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/validation.md b/validation.md index f4f52e69e8..ef18ae4388 100644 --- a/validation.md +++ b/validation.md @@ -1677,13 +1677,10 @@ You may explicitly specify the database column name that should be used by the ` 'state' => Rule::exists('states', 'abbreviation'), ``` - -#### Validating an Array of Values - Sometimes, you may wish to validate whether an array of values exists in the database. You can do so by adding both the `exists` and [array](#rule-array) rules to the field being validated: ```php -'user_ids' => ['array', 'exists:users,id'], +'states' => ['array', Rule::exists('states', 'abbreviation')], ``` When both of these rules are assigned to a field, Laravel will automatically build a single query to determine if all of the given values exist in the specified table. From bfab8a77491aba21b08a071c213691b2732fee89 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 13:20:20 -0700 Subject: [PATCH 100/162] document useStream --- responses.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/responses.md b/responses.md index fbf1516f9d..a60b6db710 100644 --- a/responses.md +++ b/responses.md @@ -428,7 +428,87 @@ yield new StreamedEvent( ); ``` -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: +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 +npm install --save @laravel/stream-react +``` + +```shell tab=Vue +npm install --save @laravel/stream-vue +``` + +Then, `useStream` may be used to consume the event stream. After providing your stream URL, the hook will automatically update the `message` with the concatenated response as messages are returned from your Laravel application: + +```jsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { message } = useStream("/chat"); + + return
{message}
; +} +``` + +```vue tab=Vue + + + +``` + +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: + +```jsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { message } = useStream("/stream", { + event: "update", + onMessage: (message) => { + // + }, + onError: (error) => { + // + }, + onComplete: () => { + // + }, + endSignal: "", + glue: " ", + }); + + return
{message}
; +} +``` + +```vue tab=Vue + +``` + +Event streams may also be manually 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'); From 2708073d24efe82a11350e9421878a3d75e797ec Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 13:46:16 -0700 Subject: [PATCH 101/162] remove save --- responses.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/responses.md b/responses.md index a60b6db710..eded68ec38 100644 --- a/responses.md +++ b/responses.md @@ -431,11 +431,11 @@ yield new StreamedEvent( 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 -npm install --save @laravel/stream-react +npm install @laravel/stream-react ``` ```shell tab=Vue -npm install --save @laravel/stream-vue +npm install @laravel/stream-vue ``` Then, `useStream` may be used to consume the event stream. After providing your stream URL, the hook will automatically update the `message` with the concatenated response as messages are returned from your Laravel application: From 00574e6741359d818f8654df53d5402a41cdfd4f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 May 2025 16:23:16 -0700 Subject: [PATCH 102/162] update hook --- responses.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/responses.md b/responses.md index eded68ec38..3f95d865c1 100644 --- a/responses.md +++ b/responses.md @@ -438,13 +438,13 @@ npm install @laravel/stream-react npm install @laravel/stream-vue ``` -Then, `useStream` may be used to consume the event stream. After providing your stream URL, the hook will automatically update the `message` with the concatenated response as messages are returned from your Laravel application: +Then, `useEventStream` may be used to consume the event stream. After providing your stream URL, the hook will automatically update the `message` with the concatenated response as messages are returned from your Laravel application: ```jsx tab=React -import { useStream } from "@laravel/stream-react"; +import { useEventStream } from "@laravel/stream-react"; function App() { - const { message } = useStream("/chat"); + const { message } = useEventStream("/chat"); return
{message}
; } @@ -452,9 +452,9 @@ function App() { ```vue tab=Vue ``` -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: +The second argument given to `useEventStream` is an options object that you may use to customize the stream consumption behavior. The default values for this object are shown below: ```jsx tab=React -import { useStream } from "@laravel/stream-react"; +import { useEventStream } from "@laravel/stream-react"; function App() { - const { message } = useStream("/stream", { + const { message } = useEventStream("/stream", { event: "update", onMessage: (message) => { // @@ -489,9 +489,9 @@ function App() { ```vue tab=Vue ``` @@ -942,6 +1092,167 @@ Echo.channel('orders') }); ``` + +### Using React or Vue + +Laravel Echo includes React and Vue hooks that make it painless to listen for events. To get started, invoke the `useEcho` hook, which is used to listen for private events. The `useEcho` will hook will automatically leave channels when the consuming component is unmounted: + +```js tab=React +import { useEcho } from "@laravel/echo-react"; + +useEcho( + `orders.${orderId}`, + "OrderShipmentStatusUpdated", + (e) => { + console.log(e.order); + }, +); +``` + +```vue tab=Vue + +``` + +You may listen to multiple events by providing an array of events to `useEcho`: + +```js +useEcho( + `orders.${orderId}`, + ["OrderShipmentStatusUpdated", "OrderShipped"], + (e) => { + console.log(e.order); + }, +); +``` + +You may also specify the shape of the broadcast event payload data, providing greater type safety and editing convenience: + +```ts +type OrderData = { + order: { + id: number; + user: { + id: number; + name: string; + }; + created_at: string; + }; +}; + +useEcho(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => { + console.log(e.order.id); + console.log(e.order.user.id); +}); +``` + +The `useEcho` will hook will automatically leave channels when the consuming component is unmounted; however, you may utilize the returned functions to manually stop / start listening to channels programmatically when necessary: + +```js tab=React +import { useEcho } from "@laravel/echo-react"; + +const { leaveChannel, leave, stopListening, listen } = useEcho( + `orders.${orderId}`, + "OrderShipmentStatusUpdated", + (e) => { + console.log(e.order); + }, +); + +// Stop listening without leaving channel... +stopListening(); + +// Start listening again... +listen(); + +// Leave channel... +leaveChannel(); + +// Leave a channel and also its associated private and presence channels... +leave(); +``` + +```vue tab=Vue + +``` + + +#### Connecting to Public Channels + +To connect to a public channel, you may use the `useEchoPublic` hook: + +```js tab=React +import { useEchoPublic } from "@laravel/echo-react"; + +useEchoPublic("posts", "PostPublished", (e) => { + console.log(e.post); +}); +``` + +```vue tab=Vue + +``` + + +#### Connecting to Presence Channels + +To connect to a presence channel, you may use the `useEchoPresence` hook: + +```js tab=React +import { useEchoPresence } from "@laravel/echo-react"; + +useEchoPresence("posts", "PostPublished", (e) => { + console.log(e.post); +}); +``` + +```vue tab=Vue + +``` + ## Presence Channels @@ -1210,11 +1521,49 @@ Once you have obtained a channel instance, you may use the `listen` method to li ```js Echo.private(`App.Models.User.${this.user.id}`) - .listen('.PostUpdated', (e) => { + .listen('.UserUpdated', (e) => { console.log(e.model); }); ``` + +#### Using React or Vue + +If you are using React or Vue, you may use Laravel Echo's included `useEchoModel` hook to easily listen for model broadcasts: + +```js tab=React +import { useEchoModel } from "@laravel/echo-react"; + +useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => { + console.log(e.model); +}); +``` + +```vue tab=Vue + +``` + +You may also specify the shape of the model event payload data, providing greater type safety and editing convenience: + +```ts +type User = { + id: number; + name: string; + email: string; +}; + +useEchoModel("App.Models.User", userId, ["UserUpdated"], (e) => { + console.log(e.model.id); + console.log(e.model.name); +}); +``` + ## Client Events @@ -1225,22 +1574,70 @@ Sometimes you may wish to broadcast an event to other connected clients without To broadcast client events, you may use Echo's `whisper` method: -```js +```js tab=JavaScript Echo.private(`chat.${roomId}`) .whisper('typing', { name: this.user.name }); ``` +```js tab=React +import { useEcho } from "@laravel/echo-react"; + +const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => { + console.log('Chat event received:', e); +}); + +channel().whisper('typing', { name: user.name }); +``` + +```vue tab=Vue + +``` + To listen for client events, you may use the `listenForWhisper` method: -```js +```js tab=JavaScript Echo.private(`chat.${roomId}`) .listenForWhisper('typing', (e) => { console.log(e.name); }); ``` +```js tab=React +import { useEcho } from "@laravel/echo-react"; + +const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => { + console.log('Chat event received:', e); +}); + +channel().listenForWhisper('typing', (e) => { + console.log(e.name); +}); +``` + +```vue tab=Vue + +``` + ## Notifications @@ -1248,11 +1645,33 @@ By pairing event broadcasting with [notifications](/docs/{{version}}/notificatio Once you have configured a notification to use the broadcast channel, you may listen for the broadcast events using Echo's `notification` method. Remember, the channel name should match the class name of the entity receiving the notifications: -```js +```js tab=JavaScript Echo.private(`App.Models.User.${userId}`) .notification((notification) => { console.log(notification.type); }); ``` +```js tab=React +import { useEchoModel } from "@laravel/echo-react"; + +const { channel } = useEchoModel('App.Models.User', userId); + +channel().notification((notification) => { + console.log(notification.type); +}); +``` + +```vue tab=Vue + +``` + 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 c10b7f7c1b139e682e9c096cbc6fd1a525b31139 Mon Sep 17 00:00:00 2001 From: Stanislav Plakhin Date: Wed, 14 May 2025 03:25:11 +0500 Subject: [PATCH 115/162] fix typo in broadcasting.md (#10398) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 6dc1861169..d7fa0b543b 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -1095,7 +1095,7 @@ Echo.channel('orders') ### Using React or Vue -Laravel Echo includes React and Vue hooks that make it painless to listen for events. To get started, invoke the `useEcho` hook, which is used to listen for private events. The `useEcho` will hook will automatically leave channels when the consuming component is unmounted: +Laravel Echo includes React and Vue hooks that make it painless to listen for events. To get started, invoke the `useEcho` hook, which is used to listen for private events. The `useEcho` hook will automatically leave channels when the consuming component is unmounted: ```js tab=React import { useEcho } from "@laravel/echo-react"; From ec7bd9468d17ec52ad7dfc6c3409d793630754e4 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 14 May 2025 01:57:48 +0330 Subject: [PATCH 116/162] Update collections.md (#10396) --- collections.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/collections.md b/collections.md index 488d82b042..39b25b5f56 100644 --- a/collections.md +++ b/collections.md @@ -589,6 +589,10 @@ collect(['1'])->containsOneItem(); collect(['1', '2'])->containsOneItem(); // false + +collect([1, 2, 3])->containsOneItem(fn ($item) => $item === 2); + +// true ``` From 14e9dae7e380a76ee6e376accc90b3bfa4622b12 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 15 May 2025 18:06:41 +0300 Subject: [PATCH 117/162] Fix typo in broadcasting.md (#10401) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index d7fa0b543b..36ebd35e1b 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -1155,7 +1155,7 @@ useEcho(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => { }); ``` -The `useEcho` will hook will automatically leave channels when the consuming component is unmounted; however, you may utilize the returned functions to manually stop / start listening to channels programmatically when necessary: +The `useEcho` hook will automatically leave channels when the consuming component is unmounted; however, you may utilize the returned functions to manually stop / start listening to channels programmatically when necessary: ```js tab=React import { useEcho } from "@laravel/echo-react"; From d26770d17f9311b378334847f31658c5ed66e735 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 15 May 2025 21:21:11 +0300 Subject: [PATCH 118/162] Add Missing Type Hints (#10402) --- collections.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/collections.md b/collections.md index 39b25b5f56..b722914326 100644 --- a/collections.md +++ b/collections.md @@ -1492,7 +1492,7 @@ The `intersectUsing` method removes any values from the original collection that ```php $collection = collect(['Desk', 'Sofa', 'Chair']); -$intersect = $collection->intersectUsing(['desk', 'chair', 'bookcase'], function ($a, $b) { +$intersect = $collection->intersectUsing(['desk', 'chair', 'bookcase'], function (string $a, string $b) { return strcasecmp($a, $b); }); @@ -1540,7 +1540,7 @@ $intersect = $collection->intersectAssocUsing([ 'color' => 'blue', 'size' => 'M', 'material' => 'polyester', -], function ($a, $b) { +], function (string $a, string $b) { return strcasecmp($a, $b); }); @@ -2110,7 +2110,7 @@ The `percentage` method may be used to quickly determine the percentage of items ```php $collection = collect([1, 1, 2, 2, 2, 3]); -$percentage = $collection->percentage(fn ($value) => $value === 1); +$percentage = $collection->percentage(fn (int $value) => $value === 1); // 33.33 ``` @@ -2118,7 +2118,7 @@ $percentage = $collection->percentage(fn ($value) => $value === 1); 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); +$percentage = $collection->percentage(fn (int $value) => $value === 1, precision: 3); // 33.333 ``` From fab57b4f5a7b2ba923b65283c2668b1e1871024e Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 16 May 2025 00:15:46 +0300 Subject: [PATCH 119/162] Add Missing Type Hints (#10403) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index b722914326..9bbcf2a15e 100644 --- a/collections.md +++ b/collections.md @@ -590,7 +590,7 @@ collect(['1', '2'])->containsOneItem(); // false -collect([1, 2, 3])->containsOneItem(fn ($item) => $item === 2); +collect([1, 2, 3])->containsOneItem(fn (int $item) => $item === 2); // true ``` From 81412add5f82963230bc73948be00356c02bc2bd Mon Sep 17 00:00:00 2001 From: Amir Hossein Shokri <123773211+amirhshokri@users.noreply.github.com> Date: Fri, 16 May 2025 00:47:58 +0330 Subject: [PATCH 120/162] [12.x] Clarify retryUntil() precedence over tries in queue jobs (#10404) * 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 73cf74504c..810a6b7aff 100644 --- a/queues.md +++ b/queues.md @@ -1253,6 +1253,8 @@ public function retryUntil(): DateTime } ``` +If both `retryUntil` and `tries` are defined, Laravel gives precedence to the `retryUntil` method. + > [!NOTE] > You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). From 87d26fb4e3cd9a9bc69c6fa3e9f11fce8db3f0d8 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Fri, 16 May 2025 00:56:43 +0330 Subject: [PATCH 121/162] [12.x] Add `deleteWhen` method in job middleware (#10400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update queues.md * Update queues.md Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> * Update queues.md --------- Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> Co-authored-by: Taylor Otwell --- queues.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/queues.md b/queues.md index 810a6b7aff..bb47dd6625 100644 --- a/queues.md +++ b/queues.md @@ -738,6 +738,23 @@ public function middleware(): array } ``` +Unlike the `when` method, which releases the job back onto the queue or throws an exception, the `deleteWhen` method allows you to delete the job entirely when a given exception occurs: + +```php +use App\Exceptions\CustomerDeletedException; +use Illuminate\Queue\Middleware\ThrottlesExceptions; + +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new ThrottlesExceptions(2, 10 * 60))->deleteWhen(CustomerDeletedException::class)]; +} +``` + 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`: ```php From ae9a6f5ede1d0dd966d145d03ae9fdfcb8c3f902 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 16 May 2025 17:19:28 +0300 Subject: [PATCH 122/162] Add exceptHidden method in Context (#10405) --- context.md | 1 + 1 file changed, 1 insertion(+) diff --git a/context.md b/context.md index dd3b2f9528..43bd805a8e 100644 --- a/context.md +++ b/context.md @@ -374,6 +374,7 @@ Context::getHidden(/* ... */); Context::pullHidden(/* ... */); Context::popHidden(/* ... */); Context::onlyHidden(/* ... */); +Context::exceptHidden(/* ... */); Context::allHidden(/* ... */); Context::hasHidden(/* ... */); Context::forgetHidden(/* ... */); From 69cdca13a65def2a8166f16f88303fb6ccb14cc9 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:14:30 +0300 Subject: [PATCH 123/162] Add Example for importing PHP constants in blade (#10413) --- blade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/blade.md b/blade.md index a483b2abe8..ad0d458a75 100644 --- a/blade.md +++ b/blade.md @@ -662,6 +662,7 @@ The `@use` directive also supports importing PHP functions and constants by pref ```blade @use(function App\Helpers\format_currency) +@use(const App\Constants\MAX_ATTEMPTS) ``` Just like class imports, aliases are supported for functions and constants as well: From d9a7fc996583d18993c5ee3145d97b91e2ed9e62 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:15:44 +0300 Subject: [PATCH 124/162] Fix incorrect PHP code (#10410) --- events.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/events.md b/events.md index e08e7989d9..c97230e887 100644 --- a/events.md +++ b/events.md @@ -866,7 +866,9 @@ test('orders can be processed', function () { Event::assertDispatched(OrderCreated::class); // Other events are dispatched as normal... - $order->update([...]); + $order->update([ + // ... + ]); }); ``` @@ -885,7 +887,9 @@ public function test_orders_can_be_processed(): void Event::assertDispatched(OrderCreated::class); // Other events are dispatched as normal... - $order->update([...]); + $order->update([ + // ... + ]); } ``` @@ -919,7 +923,9 @@ test('orders can be processed', function () { }); // Events are dispatched as normal and observers will run ... - $order->update([...]); + $order->update([ + // ... + ]); }); ``` @@ -949,7 +955,9 @@ class ExampleTest extends TestCase }); // Events are dispatched as normal and observers will run ... - $order->update([...]); + $order->update([ + // ... + ]); } } ``` From d529394000202c92c2e0d0f2cd4b39afbec9a50c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:17:14 +0300 Subject: [PATCH 125/162] [12.x] Add a default value to the `enum` method (#10412) * Add a default value to the `enum` method * 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 01fe612622..78154a8166 100644 --- a/requests.md +++ b/requests.md @@ -422,6 +422,12 @@ use App\Enums\Status; $status = $request->enum('status', Status::class); ``` +You may also provide a default value that will be returned if the value is missing or invalid: + +```php +$status = $request->enum('status', Status::class, Status::Pending); +``` + 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: ```php From a08fd47c136679eb95ff95cfe4af61126ca0e039 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:17:57 +0300 Subject: [PATCH 126/162] Add missingHidden method in Context (#10411) --- context.md | 1 + 1 file changed, 1 insertion(+) diff --git a/context.md b/context.md index 43bd805a8e..7d90914d33 100644 --- a/context.md +++ b/context.md @@ -377,6 +377,7 @@ Context::onlyHidden(/* ... */); Context::exceptHidden(/* ... */); Context::allHidden(/* ... */); Context::hasHidden(/* ... */); +Context::missingHidden(/* ... */); Context::forgetHidden(/* ... */); ``` From 60ecc14e1364ffaed18ff3f5704cd5e8da28dd19 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:18:54 +0300 Subject: [PATCH 127/162] Fix spacing inconsistency before ellipsis in event comments (#10409) Co-authored-by: Taylor Otwell --- events.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/events.md b/events.md index c97230e887..ff4a71adf7 100644 --- a/events.md +++ b/events.md @@ -922,7 +922,7 @@ test('orders can be processed', function () { return $order; }); - // Events are dispatched as normal and observers will run ... + // Events are dispatched as normal and observers will run... $order->update([ // ... ]); @@ -954,7 +954,7 @@ class ExampleTest extends TestCase return $order; }); - // Events are dispatched as normal and observers will run ... + // Events are dispatched as normal and observers will run... $order->update([ // ... ]); From 3c0bfd38b414133db9065f2415cf264d79c13f7f Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 19 May 2025 16:22:38 +0300 Subject: [PATCH 128/162] Fix hyphenation of closure-based for grammatical correctness and consistency (#10406) --- artisan.md | 6 +++--- eloquent-factories.md | 6 +++--- events.md | 2 +- pennant.md | 2 +- structure.md | 2 +- views.md | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/artisan.md b/artisan.md index 52efee9645..9aea15abe3 100644 --- a/artisan.md +++ b/artisan.md @@ -184,9 +184,9 @@ $this->fail('Something went wrong.'); ### 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. +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 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: ```php Artisan::command('mail:send {user}', function (string $user) { @@ -213,7 +213,7 @@ Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) #### Closure Command Descriptions -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: +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: ```php Artisan::command('mail:send {user}', function (string $user) { diff --git a/eloquent-factories.md b/eloquent-factories.md index afb6383f02..6847dd1813 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -365,7 +365,7 @@ $user = User::factory() ->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: +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: ```php $user = User::factory() @@ -400,7 +400,7 @@ $user = User::factory() ->create(); ``` -You may provide a closure based state transformation if your state change requires access to the parent model: +You may provide a closure-based state transformation if your state change requires access to the parent model: ```php $user = User::factory() @@ -483,7 +483,7 @@ $user = User::factory() ->create(); ``` -You may provide a closure based state transformation if your state change requires access to the related model: +You may provide a closure-based state transformation if your state change requires access to the related model: ```php $user = User::factory() diff --git a/events.md b/events.md index ff4a71adf7..076d88f697 100644 --- a/events.md +++ b/events.md @@ -160,7 +160,7 @@ public function boot(): void #### 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): +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): ```php use App\Events\PodcastProcessed; diff --git a/pennant.md b/pennant.md index ba84e24595..4f7734d422 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 diff --git a/structure.md b/structure.md index 452d16bbdf..2218e1a24d 100644 --- a/structure.md +++ b/structure.md @@ -72,7 +72,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. You may also [schedule](/docs/{{version}}/scheduling) tasks in the `console.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. 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. diff --git a/views.md b/views.md index f162919bf8..4904644896 100644 --- a/views.md +++ b/views.md @@ -200,7 +200,7 @@ class AppServiceProvider extends ServiceProvider // Using class based composers... Facades\View::composer('profile', ProfileComposer::class); - // Using closure based composers... + // Using closure-based composers... Facades\View::composer('welcome', function (View $view) { // ... }); From 3492a019babd9604a5f1f0d8436cab63f6ccb761 Mon Sep 17 00:00:00 2001 From: Shane Date: Mon, 19 May 2025 22:15:51 +0800 Subject: [PATCH 129/162] [12.x] Add `assertClientError` method to http-tests (#10416) --- http-tests.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/http-tests.md b/http-tests.md index 7a5dabbb86..003bdc4859 100644 --- a/http-tests.md +++ b/http-tests.md @@ -961,6 +961,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertAccepted](#assert-accepted) [assertBadRequest](#assert-bad-request) +[assertClientError](#assert-client-error) [assertConflict](#assert-conflict) [assertCookie](#assert-cookie) [assertCookieExpired](#assert-cookie-expired) @@ -1055,6 +1056,15 @@ Assert that the response has an accepted (202) HTTP status code: $response->assertAccepted(); ``` + +#### assertClientError + +Assert that the response has a client error (>= 400 , < 500) HTTP status code: + +```php +$response->assertClientError(); +``` + #### assertConflict From 6d3e2bdc05cb0c31b4291d340a82f817f51ecff7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 May 2025 09:16:21 -0500 Subject: [PATCH 130/162] wip --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index 003bdc4859..e5f34c8ffa 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1059,7 +1059,7 @@ $response->assertAccepted(); #### assertClientError -Assert that the response has a client error (>= 400 , < 500) HTTP status code: +Assert that the response has a client error (>= 400, < 500) HTTP status code: ```php $response->assertClientError(); From 6d3a4a802d063258b5beae46cc39f1a6f58504e7 Mon Sep 17 00:00:00 2001 From: Shane Date: Mon, 19 May 2025 22:33:46 +0800 Subject: [PATCH 131/162] [12.x] reorder assertions alphabetically (#10417) --- http-tests.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/http-tests.md b/http-tests.md index e5f34c8ffa..c12ad81704 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1038,22 +1038,22 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
- -#### assertBadRequest + +#### assertAccepted -Assert that the response has a bad request (400) HTTP status code: +Assert that the response has an accepted (202) HTTP status code: ```php -$response->assertBadRequest(); +$response->assertAccepted(); ``` - -#### assertAccepted + +#### assertBadRequest -Assert that the response has an accepted (202) HTTP status code: +Assert that the response has a bad request (400) HTTP status code: ```php -$response->assertAccepted(); +$response->assertBadRequest(); ``` From 4bb6103798c5083704b907f04c9ee152e5354a58 Mon Sep 17 00:00:00 2001 From: Amir Hossein Shokri <123773211+amirhshokri@users.noreply.github.com> Date: Tue, 20 May 2025 18:29:18 +0330 Subject: [PATCH 132/162] Update queues.md (#10418) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index bb47dd6625..3b899ebd0d 100644 --- a/queues.md +++ b/queues.md @@ -1273,7 +1273,7 @@ public function retryUntil(): DateTime If both `retryUntil` and `tries` are defined, Laravel gives precedence to the `retryUntil` method. > [!NOTE] -> You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). +> You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners) and [queued notifications](/docs/{{version}}/notifications#queueing-notifications). #### Max Exceptions From 326c234542846ff4d5246ed4d31fa14cc646a670 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 May 2025 10:13:09 -0500 Subject: [PATCH 133/162] wip --- container.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index 460a8a3f8d..d60dd934da 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`, `DB`, `Log`, `RouteParameter`, and [Tag](#tagging) attributes: +In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `Context`, `DB`, `Log`, `RouteParameter`, and [Tag](#tagging) attributes: ```php Date: Tue, 20 May 2025 10:13:52 -0500 Subject: [PATCH 134/162] wip --- pagination.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pagination.md b/pagination.md index 0fa0840e4f..2f3af28955 100644 --- a/pagination.md +++ b/pagination.md @@ -275,6 +275,7 @@ The JSON from the paginator will include meta information such as `total`, `curr "per_page": 15, "current_page": 1, "last_page": 4, + "current_page_url": "http://laravel.app?page=1", "first_page_url": "http://laravel.app?page=1", "last_page_url": "http://laravel.app?page=4", "next_page_url": "http://laravel.app?page=2", From 5e6d0c2307a2b13cd7dcc2a32737c8d8fa4a6322 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 May 2025 10:15:21 -0500 Subject: [PATCH 135/162] wip --- eloquent.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index e48bae7604..8c92e4d50c 100644 --- a/eloquent.md +++ b/eloquent.md @@ -879,7 +879,7 @@ $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: +The `getChanges` method returns an array containing the attributes that changed when the model was last saved, while the `getPrevious` method returns an array containing the original attribute values before the model was last saved: ```php $user = User::find(1); @@ -900,6 +900,15 @@ $user->getChanges(); 'email' => 'jack@example.com', ] */ + +$user->getPrevious(); + +/* + [ + 'name' => 'John', + 'email' => 'john@example.com', + ] +*/ ``` From 8882e41938abdebc3f55ef334c8bc2345ffa0e8e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 22 May 2025 07:19:53 -0500 Subject: [PATCH 136/162] Use Stream Docs (#10420) * use stream docs * fix typo --------- Co-authored-by: Joe Tannenbaum --- responses.md | 347 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 333 insertions(+), 14 deletions(-) diff --git a/responses.md b/responses.md index 46f6d33ee1..3af163f404 100644 --- a/responses.md +++ b/responses.md @@ -14,7 +14,11 @@ - [JSON Responses](#json-responses) - [File Downloads](#file-downloads) - [File Responses](#file-responses) - - [Streamed Responses](#streamed-responses) +- [Streamed Responses](#streamed-responses) + - [Consuming Streamed Responses](#consuming-streamed-responses) + - [Streamed JSON Responses](#streamed-json-responses) + - [Event Streams (SSE)](#event-streams) + - [Streamed Downloads](#streamed-downloads) - [Response Macros](#response-macros) @@ -360,20 +364,15 @@ return response()->file($pathToFile, $headers); ``` -### Streamed Responses +## 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: ```php -function streamedContent(): Generator { - yield 'Hello, '; - yield 'World!'; -} - Route::get('/stream', function () { return response()->stream(function (): void { - foreach (streamedContent() as $chunk) { - echo $chunk; + foreach (['developer', 'admin'] as $string) { + echo $string; ob_flush(); flush(); sleep(2); // Simulate delay between chunks... @@ -382,11 +381,262 @@ Route::get('/stream', function () { }); ``` -> [!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. +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 () { + return response()->stream(function (): void { + $stream = OpenAI::client()->chat()->createStreamed(...); + + foreach ($stream as $response) { + yield $response->choices[0]; + } + }); +}); +``` + + +### Consuming Streamed Responses + +Streamed responses may be consumed using Laravel's `stream` npm package, which provides a convenient API for interacting with Laravel response and event streams. To get started, install the `@laravel/stream-react` or `@laravel/stream-vue` package: + +```shell tab=React +npm install @laravel/stream-react +``` + +```shell tab=Vue +npm install @laravel/stream-vue +``` + +Then, `useStream` may be used to consume the event stream. After providing your stream URL, the hook will automatically update the `data` with the concatenated response as content is returned from your Laravel application: + +```tsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { data, isFetching, isStreaming, send } = useStream("chat"); + + const sendMessage = () => { + send({ + message: `Current timestamp: ${Date.now()}`, + }); + }; + + return ( +
+
{data}
+ {isFetching &&
Connecting...
} + {isStreaming &&
Generating...
} + +
+ ); +} +``` + +```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 137/162] 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 5f4d5cfc32..5466f0fe76 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 138/162] Clarify retryUntil() precedence over tries (#10427) --- events.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/events.md b/events.md index 076d88f697..dbacb51542 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 139/162] 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 e6c31d8648..7d7df1fb60 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 140/162] 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 e29cd91671..9555904144 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 141/162] 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 9bbcf2a15e..8e010c260d 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 142/162] [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 7d90914d33..51c119cce3 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 143/162] 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 7d7df1fb60..819322de99 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 144/162] 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 9555904144..9a412ee2b6 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 145/162] 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 d60dd934da..cf5ee304ce 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 146/162] [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 f01fae8f3e..7a8fe60767 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 147/162] 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 8e010c260d..bd7a951f10 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 148/162] 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 7a8fe60767..5287340d39 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 149/162] 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 9aea15abe3..b53097ba19 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 150/162] Use environment variable (#10444) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 36ebd35e1b..7b6fe751ab 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 151/162] 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 dbacb51542..f2d8de7b88 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 152/162] 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 bd7a951f10..949f7f76f6 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 153/162] [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 819322de99..a797121cdd 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 154/162] [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 cb0c348418..94ab791d21 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 2c3ddb3e30..88af28d3c1 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 155/162] 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 f2d8de7b88..7ad8548f8b 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 156/162] 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 949f7f76f6..dafbc1de41 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 a797121cdd..a37f15f985 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 157/162] [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 b53097ba19..0e62cbed10 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 158/162] Add array method in Request (#10452) --- requests.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/requests.md b/requests.md index 78154a8166..e7476fa712 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 159/162] Update Passport documentation (#10450) --- passport.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/passport.md b/passport.md index 94ab791d21..fc0d76c636 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 160/162] wip --- validation.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/validation.md b/validation.md index ef18ae4388..931b79513e 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 161/162] wip --- strings.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/strings.md b/strings.md index 43dc92adba..30b1c32093 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 162/162] [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 931b79513e..1d4d4df2b2 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