細かすぎて伝わらないLaravel選手権8（laravel8）


Laraevl選手権8と言っていますが8回もやってません。versionに合わせました。

laravelのchangelogを眺めて、新しく追加された細かい機能を見ていこうぜ！っていうコーナーです

全部を網羅した訳ではなく、**Added**された中でも自分が気になるものをピックアップしました。

https://github.com/laravel/framework/blob/8.x/CHANGELOG-8.x.md



## paginationにlinksプロパティーが追加
- v8.0.3
- src/Illuminate/Pagination/LengthAwarePaginator.php

こんなふうにpaginateをjsonで返した場合にlinksプロパティーが追加されました。linksプロパティーの詳細は「laravel pagination カスタマイズ」で検索！

```php
Route::get('/users', function () {
    return User::paginate();
});
```

```php
{
   "total": 50,
   "per_page": 15,
   "current_page": 1,
   "last_page": 4,
   "first_page_url": "http://laravel.app?page=1",
   "last_page_url": "http://laravel.app?page=4",
   "next_page_url": "http://laravel.app?page=2",
   "prev_page_url": null,
   "path": "http://laravel.app",
   "from": 1,
   "to": 15,
   "data":[
        {
            // レコード…
        },
        {
            // レコード…
        }
   ],
   // ↓追加された
   "links": [
      {
        "url": null,
        "label": "&laquo; Previous",
        "active": false
      },
      {
        "url": "http://laravel.app?page=1",
        "label": "1",
        "active": true
      },
      {
        "url": null,
        "label": "Next &raquo;",
        "active": false
      }
    ],
}
```

https://github.com/laravel/framework/commit/13751a187834fabe515c14fb3ac1dc008fd23f37


## Testクラス内でjson型のカラムと比較できる`castAsJson()`メソッドが追加
- v8.3.0
- src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php

```php
$this->assertDatabaseHas('users', [
    'name' => 'Peter Parker',
    'email' => 'spidey@yahoo.com',
    'skills' => $this->castAsJson(json_encode(['web slinging', 'spidey-sense', 'sticky feet'])),
]);
```

https://github.com/laravel/framework/pull/34302

## キュー処理のジョブバッチで使用するbatchメソッドにクロージャ（無名関数）を渡せるように
- v8.3.0
- src/Illuminate/Bus/Batch.php

```php
Bus::batch([
    new ProcessPodcast,
    function () {
        // ...
    },
    new ReleasePodcast
])->dispatch();
```

https://github.com/laravel/framework/pull/34333


## ジョブをクリアできる`php artisan queue:clear`コマンドが追加
- v8.4.0
- src/Illuminate/Queue/Console/ClearCommand.php

ジョブ（jobsテーブル）はクリアできるけど、ジョブバッチ（job_batchesテーブル）には残るので注意です！

v8.21.0からは別コマンドでジョブバッチもクリアできます


```bash
php artisan queue:clear
```

https://github.com/laravel/framework/pull/34330


## クエリービルダに`crossJoinSub()`メソッド追加
- v8.5.0
- src/Illuminate/Database/Query/Builder.php

joinSub、leftJoinSub、rightJoinSubに加えてcrossJoinSubが追加

https://github.com/laravel/framework/pull/34400

https://readouble.com/laravel/8.x/ja/queries#subquery-joins


## LazyCollectionにタイムアウトを設定できるように
- v8.6.0
- src/Illuminate/Collections/LazyCollection.php

LazyCollectionにタイムアウトを設定できるようになりました。  

```php
$lazyCollection
    ->takeUntilTimeout(now()->add(2, 'minutes'))
    ->each(fn ($item) => doSomethingThatMayTakeSomeTime($item));
 
// ^^ This will only process items for up to 2 minutes ^^
```

## Httpクライアントでのレスポンスエラー発生時に`onError()`でコールバック処理ができるように
- v8.7.0
- src/Illuminate/Http/Client/Response.php

`throw()`との違いは例外を投げるかどうかです。

Before

```php
$response = $client->withHeaders($headers)->post($url, $payload);

if ($response->failed()) {
    Log::error('Twitter API failed posting Tweet', [
        'url' => $url,
        'payload' => $payload,
        'headers' => $headers,
        'response' => $response->body(),
    ]);

    $response->throw();
}

return $response->json();
```

After

```php
return $client->withHeaders($headers)
    ->post($url, $payload)
    ->onError(fn ($response) =>
        Log::error('Twitter API failed posting Tweet', [
            'url' => $url,
            'payload' => $payload,
            'headers' => $headers,
            'response' => $response->body(),
        ])
    )->throw()->json();
```


https://github.com/laravel/framework/pull/34558

## Collectionに`pipeInto()`メソッドが追加されました
- v8.8.0
- src/Illuminate/Collections/Traits/EnumeratesValues.php

APIリソースを返す時に使えそうです

#### pipeInto
```php
class ResourceCollection
{
    /**
     * コレクションインスタンス
     */
    public $collection;

    /**
     * 新しいResourceCollectionインスタンスの生成
     *
     * @param  Collection  $collection
     * @return void
     */
    public function __construct(Collection $collection)
    {
        $this->collection = $collection;
    }
}

$collection = collect([1, 2, 3]);

$resource = $collection->pipeInto(ResourceCollection::class);

$resource->collection->all();

// [1, 2, 3]
```

https://github.com/laravel/framework/pull/34600

https://readouble.com/laravel/8.x/ja/collections.html#method-pipeinto

## HttpクライアントでuserAgentをセットする`withUserAgent()`メソッドが追加
- v8.8.0
- src/Illuminate/Collections/Traits/EnumeratesValues.php

地味だけど便利

Before

```php
Http::withHeaders(['User-Agent' => $userAgent])->get($url);
```

After

```php
Http::withUserAgent($userAgent)->get($url);
```

https://github.com/laravel/framework/pull/34611


## スケジュールをローカルで実行できる`php artisan schedule:work`が追加

今まではスケジュールを動かすのに`schedule:run`コマンドをcronに登録しておく必要がありましたが、ローカルからフォアグラウンドで動かしておける`schedule:work`が追加されました。

これは結構便利ですね

https://github.com/laravel/framework/pull/34618

https://readouble.com/laravel/8.x/ja/scheduling.html#running-the-scheduler-locally


## paginatorのアイテムを変換できる`through()`メソッドが追加
- v8.9.0
- src/Illuminate/Pagination/AbstractPaginator.php

今までは`$paginator->getCollection()->transform()`なんてしていましたが、その必要も無くなりました！

```php
return Inertia::render('Contacts/Index', [
    'contacts' => Contact::paginate()->through(function ($contact) {
        return [
            'id' => $contact->id,
            'name' => $contact->name,
            'phone' => $contact->phone,
            'city' => $contact->city,
            'organization' => optional($contact->organization)->only('name')
        ];
    }),
]);
```

https://github.com/laravel/framework/pull/34657


## Eloquentやクエリービルダでupsertが使えるように
- v8.10.0
- src/Illuminate/Database/Query/Builder.php

MySQLの場合on duplicate key updateを使っており、uniqueかprimary keyが必要なので注意！

```php
User::upsert([
    ['id' => 1, 'email' => 'taylor@example.com'],
    ['id' => 2, 'email' => 'dayle@example.com'],
], 'email');
```

https://github.com/laravel/framework/pull/34698

https://readouble.com/laravel/8.x/ja/queries.html#upserts


## バリデーションで倍数のチェックができるように
- v8.10.0
- src/Illuminate/Validation/Concerns/ValidatesAttributes.php

```html
<input type="number" step="0.5" name="foo">
```

```php
public function rules(): array
{
    return [
        'foo' => [
            'multiple_of:0.5',
        ]
    ];
}
```

https://github.com/laravel/framework/pull/34788

## マイグレーションで外部キー制約のカラム削除が簡単に
- v8.10.0
- src/Illuminate/Database/Schema/Blueprint.php

これも地味に便利

Before

```php
class AddCategoryIdToPostsTable extends Migration
{
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropForeign(['category_id']);
            $table->dropColumn('category_id');
        });
    }
}
```

After

```php
class AddCategoryIdToPostsTable extends Migration
{
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropConstrainedForeignId('category_id');
        });
    }
}
```

https://github.com/laravel/framework/pull/34806


## Eloquentのcastsに`encrypted`が使えるように
- v8.12.0
- src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

以前はアクセサやカスタムキャストを作る必要がありましたが、直接指定できるように

```php
public $casts = [
    'access_token' => 'encrypted',
];
```

https://github.com/laravel/framework/pull/34937

## リレーションのクエリに`withMax()`|`withMin()`|`withSum()`|`withAvg()`メソッドが追加
- v8.12.0
- src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php

これは結構便利ですね。ただ発行されるSQLはゴリゴリのサブクエリなので注意

```php
Post::withCount('comments');
Post::withMax('comments', 'created_at');
Post::withMin('comments', 'created_at');
Post::withSum('comments', 'foo');
Post::withAvg('comments', 'foo');
```

https://github.com/laravel/framework/pull/34965

https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#other-aggregate-functions


## Eloquentやクエリービルダでexplainが使えるように
- v8.12.0
- src/Illuminate/Database/Concerns/ExplainsQueries.php

便利！

```php
>>> DB::table('users')->where('name', 'Illia Sakovich')->explain()
=> Illuminate\Support\Collection {#5348
     all: [
       {#5342
         +"id": 1,
         +"select_type": "SIMPLE",
         +"table": "users",
         +"partitions": null,
         +"type": "ALL",
         +"possible_keys": null,
         +"key": null,
         +"key_len": null,
         +"ref": null,
         +"rows": 9,
         +"filtered": 11.11111164093,
         +"Extra": "Using where",
       },
     ],
   }
```

https://github.com/laravel/framework/pull/34969

## ルーティングに正規表現メソッドが追加
- v8.12.0
- src/Illuminate/Routing/RouteRegexConstraintTrait.php

`whereNumber`|`whereAlpha`|`whereAlphaNumeric`|`whereUuid`などが使えるようです

Before

```php
Route::get('authors/{author}/{book}')->where(['author' => '[0-9]+', 'book' => '[a-zA-Z]+']);
```

After

```php
Route::get('authors/{author}/{book}')->whereNumber('author')->whereString('book');
```

https://github.com/laravel/framework/pull/34997

https://readouble.com/laravel/8.x/ja/routing.html#parameters-regular-expression-constraints


## EloquentのCollectionに`loadMax()`|`loadMin()`|`loadSum()`|`loadAvg()`メソッド追加。Eloquentに`loadMax()`|`loadMin()`|`loadSum()`|`loadAvg()`|`loadMorphMax()`|`loadMorphMin()`|`loadMorphSum()`|`loadMorphAvg()`メソッド追加
- v8.13.0
- src/Illuminate/Database/Eloquent/Collection.php
- src/Illuminate/Database/Eloquent/Model.php

これも便利ですね。ただゴリゴリのサブクエリ（略

```php
    //Eloquent/Collection

    public function loadAggregate($relations, $column, $function = null) {...}
    public function loadCount($relations) {...} //Just modified.
    public function loadMax($relations, $column)  {...}
    public function loadMin($relations, $column)  {...}
    public function loadSum($relations, $column)  {...}
    public function loadAvg($relations, $column)  {...}
```

```php
    //Eloquent/Model

    public function loadAggregate($relations, $column, $function = null) {...}
    public function loadCount($relations) {...} //Just modified.
    public function loadMax($relations, $column) {...}
    public function loadMin($relations, $column) {...}
    public function loadSum($relations, $column) {...}
    public function loadAvg($relations, $column) {...}

    public function loadMorphAggregate($relation, $relations, $column, $function = null) {...}
    public function loadMorphCount($relation, $relations) {...} //Just modified.
    public function loadMorphMax($relation, $relations, $column) {...}
    public function loadMorphMin($relation, $relations, $column) {...}
    public function loadMorphSum($relation, $relations, $column) {...}
    public function loadMorphAvg($relation, $relations, $column) {...}
```

こんな感じで使える

```php
    $user = User::find(1);
    $user->loadCount('posts');
    $user->loadMax('posts', 'created_at');
```

https://github.com/laravel/framework/pull/35029

https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#deferred-count-loading


## Eloquentのcasts`encrypted`で使用する暗号鍵を設定できる`Model::encryptUsing()`が追加
- v8.14.0
- src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

```php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Encryption\Encrypter;

$databaseEncryptionKey = config('database.encryption_key');

$encrypter = new Encrypter($databaseEncryptionKey);

Model::encryptUsing($encrypter);
```

https://github.com/laravel/framework/pull/35080

## HttpのResponseを返す際に指定のクッキーを無くす`withoutCookie()`メソッドが追加
- v8.15.0
- src/Illuminate/Http/ResponseTrait.php

単純に有効期限切れにする感じですね

```php
    /**
     * Expire a cookie when sending the response.
     *
     * @param  \Symfony\Component\HttpFoundation\Cookie|mixed  $cookie
     * @param  string|null $path
     * @param  string|null $domain
     * @return $this
     */
    public function withoutCookie($cookie, $path = null, $domain = null)
    {
        if (is_string($cookie) && function_exists('cookie')) {
            $cookie = cookie($cookie, null, -2628000, $path, $domain);
        }

        $this->headers->setCookie($cookie);

        return $this;
    }
```

https://github.com/laravel/framework/commit/e9483c441d5f0c8598d438d6024db8b1a7aa55fe

## アップロードファイルのテスト時にexistsと合わせてcontentもチェックできるように
- v8.15.0
- src/Illuminate/Filesystem/FilesystemAdapter.php

これも便利

Before

```php
Storage::disk('reports')->assertExists('foo.csv');
$this->assertSame('my;csv;content', Storage::disk('reports')->read('foo.csv'));
```

After

```php
Storage::disk('reports')->assertExists('foo.csv', 'my;csv;content');
```

## Collectionで複数ソートができるように
- v8.16.0
- src/Illuminate/Collections/Collection.php

これはナイス

```php
$collection = collect([
    ['name' => 'Taylor Otwell', 'age' => 34],
    ['name' => 'Abigail Otwell', 'age' => 30],
    ['name' => 'Taylor Otwell', 'age' => 36],
    ['name' => 'Abigail Otwell', 'age' => 32],
]);

$sorted = $collection->sortBy([
    ['name', 'asc'],
    ['age', 'desc'],
]);

$sorted->values()->all();

/*
    [
        ['name' => 'Abigail Otwell', 'age' => 32],
        ['name' => 'Abigail Otwell', 'age' => 30],
        ['name' => 'Taylor Otwell', 'age' => 36],
        ['name' => 'Taylor Otwell', 'age' => 34],
    ]
*/
```

https://github.com/laravel/framework/pull/35277

https://readouble.com/laravel/8.x/ja/collections.html#method-sortby

## DBに接続できる`php artisan db`が追加
- v8.16.0
- src/Illuminate/Database/Console/DbCommand.php

べんり！でもmysql-clientがインストールされている必要があるので注意！

https://github.com/laravel/framework/pull/35304



## factoryで、存在する親モデルのインスタンスを紐付けするサポートが追加
- v8.18.0
- src/Illuminate/Database/Eloquent/Factories/Factory.php

factoryを作成する際にforでは親のfactoryを渡す方法だけでしたが


```php
use App\Models\Post;
use App\Models\User;

$posts = Post::factory()
            ->count(3)
            ->for(User::factory()->state([
                'name' => 'Jessica Archer',
            ]))
            ->create();
```

親のモデルインスタンスを渡して紐付けできるように

```php
$user = User::factory()->create();

$posts = Post::factory()
            ->count(3)
            ->for($user)
            ->create();
```

https://github.com/laravel/framework/pull/35494

https://readouble.com/laravel/8.x/ja/database-testing.html#belongs-to-relationships

## メール本文の内容をテストするためのメソッドがいくつか追加
- v8.18.0
- src/Illuminate/Mail/Mailable.php

これは積極的に使っていきたい


```php
use App\Mail\InvoicePaid;
use App\Models\User;

public function test_mailable_content()
{
    $user = User::factory()->create();

    $mailable = new InvoicePaid($user);

    $mailable->assertSeeInHtml($user->email);
    $mailable->assertSeeInHtml('Invoice Paid');

    $mailable->assertSeeInText($user->email);
    $mailable->assertSeeInText('Invoice Paid');
}
```


https://github.com/laravel/framework/commit/afb858ad9c944bd3f9ad56c3e4485527d77a7327

https://readouble.com/laravel/8.x/ja/mail.html#testing-mailables


## スケジュール一覧を表示する`php artisan schedule:list`が追加
- v8.19.0
- src/Illuminate/Console/Scheduling/ScheduleListCommand.php


https://github.com/laravel/framework/pull/35574

## ジョブデータを暗号化できるように
- v8.19.0
- src/Illuminate/Contracts/Queue/ShouldBeEncrypted.php

jobsテーブルのpayloadを見ると大体の内容がわかってしまうが、ShouldBeEncryptedを実装してあげれば暗号可能に

```php
use Illuminate\Contracts\Queue\ShouldBeEncrypted;

class VerifyUser implements ShouldQueue, ShouldBeEncrypted
{
    private $user;
    private $socialSecurityNumber;

    public function __construct($user, $socialSecurityNumber)
    {
        $this->user = $user;
        $this->socialSecurityNumber = $socialSecurityNumber;
    }
}
```

https://github.com/laravel/framework/pull/35527

https://divinglaravel.com/job-encryption-in-laravel


## ジョブバッチをクリアできる`php artisan queue:prune-batches`コマンドが追加
- v8.21.0
- src/Illuminate/Queue/Console/PruneBatchesCommand.php

```bash
>>> php artisan queue:prune-batches
199 entries deleted.
```

```
$schedule->command('queue:prune-batches --hours=48')->daily();
```

https://github.com/laravel/framework/pull/35694

https://readouble.com/laravel/8.x/ja/queues.html#pruning-batches

## クエリービルダに、レコードが1つだけ存在かつそれを取得する`sole()`メソッドが追加
- v8.23.0
- src/Illuminate/Database/Concerns/BuildsQueries.php

レコードが存在しない、複数レコードが存在する場合は例外を投げます。
Djangoでは`get`、Railsでは`sole`や`find_sole_by`で存在するそうです

```php
$user = User::where('name', 'test1')->sole();
```

https://github.com/laravel/framework/pull/35869

## throw_if / throw_unless がデフォルトでRuntimeExceptionを投げるように
- v8.23.0
- src/Illuminate/Support/helpers.php

これは、、、

https://github.com/laravel/framework/pull/35890


## 並列テストが可能に
- v8.25.0
- src/Illuminate/Testing/ParallelRunner.php

これはありがたい！詳しい使い方はドキュメントをチェック！

https://github.com/laravel/framework/pull/36034

https://readouble.com/laravel/8.x/ja/testing.html#running-tests-in-parallel


## Listeners, Mailables, NotificationsでShouldBeEncryptedが使えるように
- v8.25.0
- src/Illuminate/Events/CallQueuedListener.php
- src/Illuminate/Mail/SendQueuedMailable.php
- src/Illuminate/Notifications/SendQueuedNotifications.php

v8.19.0でジョブの内容を暗号化するようにリスナー、メール、通知でも使えるみたいですね

https://github.com/laravel/framework/pull/36036

## ルーティングに`missing()`メソッドが追加
- v8.26.0
- src/Illuminate/Routing/Middleware/SubstituteBindings.php

ルートモデルバインディングを使用した際に、該当のデータがない場合にModelNotFoundExceptionが投げられますが、それを任意で処理できるように


```php
Route::get('/locations/{location:slug}', [LocationsController::class, 'show'])
    ->name('locations.view')
    ->missing(fn($request) => Redirect::route('locations.index', null, 301));
```

https://github.com/laravel/framework/pull/36035

https://readouble.com/laravel/8.x/ja/routing.html#customizing-missing-model-behavior

## after columnで複数追加できるように
- v8.27.0
- src/Illuminate/Database/Schema/Blueprint.php

```php
Schema::table('users', function (Blueprint $table) {
    $table->after('remember_token', function ($table){
        $table->string('card_brand')->nullable();
        $table->string('card_last_four', 4)->nullable();
    });
});
```

https://github.com/laravel/framework/pull/36145

https://readouble.com/laravel/8.x/ja/migrations.html#column-order

## EloquentのcastにAsArrayObject、AsCollectionが使えるように
- v8.28.0
- src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php
- src/Illuminate/Database/Eloquent/Casts/AsCollection.php


castsにarrayを指定すると、jsonやtext型のフィールドに対して配列形式で保存することが可能ですが、AsArrayObjectやAsCollectionを指定することでObjectやCollectionで扱えるようになりました。詳しくはドキュメントをチェック！


```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * キャストする必要のある属性
     *
     * @var array
     */
    protected $casts = [
        'options' => AsArrayObject::class,
    ];
}
```

https://github.com/laravel/framework/pull/36245

https://readouble.com/laravel/8.x/ja/eloquent-mutators.html#array-and-json-casting

## バリデーションで最初にエラーが見つかったら即終了する`stopOnFirstFailure()`メソッドが追加
- v8.30.0
- src/Illuminate/Foundation/Http/FormRequest.php

```php
if ($validator->stopOnFirstFailure()->fails()) {
    // ...
}
```

https://github.com/laravel/framework/commit/39e1f84a48fec024859d4e80948aca9bd7878658

https://readouble.com/laravel/8.x/ja/validation.html#manually-creating-validators

## FluentなjsonのAssertが追加
- v8.32.0
- src/Illuminate/Testing/Fluent/Assert.php

Fluent（メソッドチェーンライク）にjsonのassertができるようになりました。

```php
use Illuminate\Testing\Fluent\Assert;

class PodcastsControllerTest extends TestCase
{
    public function test_can_view_podcast()
    {
        $this->get('/podcasts/41')
            ->assertJson(fn (Assert $json) => $json
                ->has('podcast', fn (Assert $json) => $json
                    ->where('id', $podcast->id)
                    ->where('subject', 'The Laravel Podcast')
                    ->where('description', 'The Laravel Podcast brings you Laravel & PHP development news.')
                    ->has('seasons', 4)
                    ->has('seasons.4.episodes', 21)
                    ->has('host', fn (Assert $json) => $json
                        ->where('id', 1)
                        ->where('name', 'Matt Stauffer')
                    )
                    ->has('subscribers', 7, fn (Assert $json) => $json
                        ->where('id', 2)
                        ->where('name', 'Claudio Dekker')
                        ->where('platform', 'Apple Podcasts')
                        ->etc()
                        ->missing('email')
                        ->missing('password')
                    )
                )
            );
    }
}
```

https://github.com/laravel/framework/pull/36454

https://readouble.com/laravel/8.x/ja/http-tests.html#fluent-json-testing

## Eloquentに`lazy()`と`lazyById()`メソッドが追加
- v8.34.0
- src/Illuminate/Database/Concerns/BuildsQueries.php

lazyを使うことで、LazyCollectionが返ってくるのでメモリ消費を抑えつつ、Collectionのように扱えるようになった！という感じですね

```php
$lazyCollection = User::lazy();
```

https://github.com/laravel/framework/pull/36699

https://readouble.com/laravel/8.x/ja/eloquent.html#streaming-results-lazily


## MySQLのdatetime型へのuseCurrentOnUpdateをサポート
- v8.36.0
- src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php


https://github.com/laravel/framework/pull/36817

https://readouble.com/laravel/8.x/ja/migrations.html#column-modifiers

## 無名クラスでマイグレーションが可能に
- v8.37.0
- src/Illuminate/Database/Migrations/Migrator.php

クラス名の衝突がなくなりますね

```php
<?php

use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
    //
};
```

https://github.com/laravel/framework/pull/36906

https://readouble.com/laravel/8.x/ja/migrations.html#anonymous-migrations


## Httpクライアントで同時非同期リクエスト処理ができるように
- v8.37.0
- src/Illuminate/Http/Client/PendingRequest.php
- src/Illuminate/Http/Client/Pool.php

これもかなり便利！

```php
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;

$responses = Http::pool(fn (Pool $pool) => [
    $pool->get('http://localhost/first'),
    $pool->get('http://localhost/second'),
    $pool->get('http://localhost/third'),
]);

return $responses[0]->ok() &&
       $responses[1]->ok() &&
       $responses[2]->ok();
```

https://github.com/laravel/framework/pull/36948

https://readouble.com/laravel/8.x/ja/http-client.html#concurrent-requests


## `Stringable::whenNotEmpty()`が追加
- v8.39.0
- src/Illuminate/Support/Stringable.php

これは汎用性高そう！

```php
Str::of(env('SCOUT_PREFIX', ''))
    ->whenNotEmpty(fn (Stringable $prefix) => $prefix->finish('_'));
```

https://github.com/laravel/framework/pull/37080

## バリデーションにPasswordルールが追加
- v8.39.0
- src/Illuminate/Validation/Rules/Password.php

こんな感じで使えて

```php
        $request->validate([

            // Makes the password require at least one uppercase and one lowercase letter.
            'password' =>  ['required', 'confirmed', Password::min(8)->mixedCase()],

             // Makes the password require at least one letter.
            'password' =>  ['required', 'confirmed', Password::min(8)->letters()],

            // Makes the password require at least one number.
            'password' =>  ['required', 'confirmed', Password::min(8)->numbers()],

            // Makes the password require at least one symbol.
            'password' =>  ['required', 'confirmed', Password::min(8)->symbols()],

            // Ensures the password has not been compromised in data leaks.
            'password' =>  ['required', 'confirmed', Password::min(8)->uncompromised()],
        ]);
```

全部を組み合わせることも可能

```php
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => ['required', 'confirmed', Password::min(8)
                    ->mixedCase()
                    ->letters()
                    ->numbers()
                    ->symbols()
                    ->uncompromised(),
            ],
        ]);
```

https://github.com/laravel/framework/pull/36960

https://readouble.com/laravel/8.x/ja/validation.html#validating-passwords

## Modelのイベントを発生せずに更新するメソッドが追加
- v8.41.0
- src/Illuminate/Database/Eloquent/Model.php

`update([])`と同じように使えます

```php
Model::updateQuietly([])
```

ちなみにsaveメソッドにはすでにあります

```php
$user = User::findOrFail(1);

$user->name = 'Victoria Faith';

$user->saveQuietly();
```

https://github.com/laravel/framework/pull/37169

https://readouble.com/laravel/8.x/ja/eloquent.html#saving-a-single-model-without-events


## カーソルページネーションが追加
- v8.41.0
- src/Illuminate/Contracts/Pagination/CursorPaginator.php

無限スクロールや、ビッグデータを扱う際のページング時に使うといいらしい

詳しくはドキュメントをチェック！

```php
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
```

https://github.com/laravel/framework/pull/37216

https://readouble.com/laravel/8.x/ja/pagination.html#cursor-pagination

## おわりに

全部は見切れませんでした！すみません！
changelogは議論も含め結構おもしろいので、Laravelっ子はぜひ見てみてください

`collect()->containsOneItem()`のくだりはさすがに笑った

認識間違っている場合はコメントくださると！


次は
[LaravelリポジトリのDiscussionされてるアイデアを読んでみる
](https://qiita.com/hiro5963/items/4b9f8d6601b053ecabe2)by @hiro5963 さん