Laravel 开源生态更新日志(2026年6月)
Laravel 开源生态的最新更新和改进。
Laravel Framework 13.x
Add Route Metadata Support
Pull request by @benbjurstrom
Routes can now carry arbitrary metadata through the full routing pipeline. Whether you're tagging routes for SEO, feature flags, or permissions, the new ->metadata() method gives you a dedicated, structured place to store that data - and it's fully compatible with route caching. Metadata set on a group merges recursively into child routes, so you can set defaults up top and override them where needed.
Route::metadata(['head' => ['robots' => ['noindex'], 'author' => 'Taylor']])
->group(function () {
Route::get('/users', [UserController::class, 'index'])
->metadata(['head' => ['title' => 'Users']]);
});
// Inside a controller or middleware
$request->route()->getMetadata('head.title'); // 'Users'
$request->route()->getMetadata('head.author', 'Taylor'); // 'Taylor'Add Postgres Transaction Pooler Support
Pull request by @DGarbs51
Running Postgres behind a transaction-mode connection pooler like PgBouncer, AWS RDS Proxy, or Neon? Laravel now supports this at the framework level. Set pooled => true in your database connection config and the framework handles emulated prepares, proper boolean binding, and everything else the pooler requires. For schema and DDL operations that need a direct connection, append ::direct to the connection name and Laravel will route around the pooler automatically - no extra configuration needed.
Should Not Retry Exception Handler
Pull request by @alexbowers
Some exceptions simply shouldn't trigger a retry - think invalid input, permanent external failures, or anything where retrying will just eat up your queue attempts with the same result. You can now define a retry() method directly on an exception class returning false, or configure non-retryable exceptions in bootstrap/app.php via withExceptions(). Either way, Laravel will respect the decision and move on without burning through your job's retry budget.
Add without-migration-data Flag to DumpCommand
Pull request by @jackbayliss
schema:dump now accepts a --without-migration-data flag that strips migration table rows from the output, leaving you with a clean structural dump. Handy for test environments where you want to seed the schema without carrying over migration history from another environment.
php artisan schema:dump --without-migration-dataMake between()/unlessBetween() Independent of timezone() Call Order
Pull request by @ManicardiFrancesco
Previously, calling between() or unlessBetween() before timezone() in a schedule chain would silently use UTC regardless of what timezone you specified - a subtle bug that could have scheduled tasks running at the wrong hour in production. The time-interval check is now deferred until the filter executes, so the order no longer matters.
// Both of these now behave identically
$schedule->command('foo')->timezone('Europe/Rome')->between('10:00', '12:00');
$schedule->command('foo')->between('10:00', '12:00')->timezone('Europe/Rome');Add artisan dev Command
Pull request by @joetannenbaum
php artisan dev brings all your development processes together in one command - server, queue worker, log tailing, and Vite running concurrently, each with its own color-coded output. It replaces the composer dev script with a proper Artisan convention and auto-detects your Node package manager (npm, yarn, pnpm, or bun).
You can register additional commands from a service provider - useful for tools like Reverb or Stripe's CLI webhook listener:
use Illuminate\Foundation\Console\DevCommands;
DevCommands::artisan('reverb:start', 'reverb')->orange();
DevCommands::register('stripe listen --forward-to ' . config('app.url'))->green();Add array Maintenance Mode Driver for Parallel Testing
Pull request by @ziadoz
Parallel test suites and the file-based maintenance mode driver don't mix well - shared state on disk leads to race conditions between workers. The new in-memory array driver keeps maintenance mode isolated per process, mirroring the same pattern used by the cache and session drivers. No more flaky tests caused by leftover maintenance mode files.
Add whenFilledEnum Method to InteractsWithData
Pull request by @astandkaya
Working with backed enums in request data used to mean a whenFilled() call followed by a tryFrom() check and a null guard. whenFilledEnum() collapses all of that into a single method that only fires the callback when the key is present, the value maps to a valid enum case, and you get a typed enum instance in the closure - no manual validation needed.
// Before
$request->whenFilled('status', function (string $input) use ($query): void {
$status = Status::tryFrom($input);
if ($status === null) {
return;
}
$query->where('status', $status);
});
// After
$request->whenFilledEnum('status', Status::class, function (Status $status) use ($query): void {
$query->where('status', $status);
});Add anyOf Support to JSON Schema
Pull request by @dbpolito
Illuminate\JsonSchema now supports anyOf, which is essential for AI structured output scenarios where a field can resolve to one of several distinct shapes. Both OpenAI and Gemini support anyOf in their structured output APIs, so this unlocks more expressive schema definitions when building AI-powered features with Laravel AI.
Support Queue Attributes on Traits
Pull request by @jackbayliss
PHP attributes like #[Queue(...)] can now be defined on traits, and Laravel will respect them when the trait is used in a job or event class. If you have queue configuration that should be shared across multiple jobs - the same connection, queue name, or timeout - define it once in a trait instead of duplicating it across every class.
#[Queue(connection: 'redis', queue: 'broadcasting')]
trait BroadcastsImports
{
public function broadcastWhen(): bool { /* ... */ }
}
class ImportCreated implements ShouldBroadcast
{
use BroadcastsImports; // picks up the Queue attribute
}Introduce Bus::bulk()
Pull request by @jackbayliss
When you need to queue a large number of jobs at once, Bus::bulk() is the most efficient way to do it. Rather than dispatching jobs one by one (one database insert each) or using a batch (which writes on every completion), Bus::bulk() groups jobs by queue and connection and performs a single bulk insert per group. No overhead, no per-job write costs.
Bus::bulk(
$users->map(fn (User $user) => new ProcessUser($user))->all()
);Added MariaDB Vector Index Capability
Pull request by @michielvaneerd
MariaDB joins PostgreSQL in supporting vector index creation through Laravel's schema builder. If you're building semantic search or AI-powered retrieval features on a MariaDB database, you can now define vector indexes directly in your migrations without dropping down to raw SQL.
Add attachFromStorage Helpers to Notification MailMessage
Pull request by @LucasCavalheri
MailMessage in notifications now has attachFromStorage() and attachFromStorageDisk() methods, matching what Mailable has offered for a while. Attach invoices, reports, or any file from your storage layer without having to read the contents yourself first.
(new MailMessage)
->attachFromStorage('invoices/1.pdf')
->attachFromStorageDisk('s3', 'invoices/1.pdf', 'Invoice.pdf', [
'mime' => 'application/pdf',
]);Add Multi-Type Union Support to Illuminate\JsonSchema
Pull request by @pushpak1300
Illuminate\JsonSchema can now represent multi-type union schemas - schemas where the type field is an array like ['string', 'number', 'boolean']. This comes up frequently with third-party MCP tool schemas where mixed types are common. Previously these would throw an exception; now they deserialize, serialize, and round-trip correctly.
// Now works where it used to throw
JsonSchema::fromArray(['type' => ['string', 'number', 'boolean']]);
// Or build one directly
JsonSchema::union(['string', 'number'])->nullable();Cache rememberWithState()
Pull request by @cosmastech
Cache::rememberWithState() returns both the cached value and a boolean indicating whether it was a hit or miss - useful for debugging, recording cache metrics, or surfacing cache status in response headers without having to check the cache twice.
Add normalize Parameter to Str::studly() and Str::pascal()
Pull request by @hotmeteor
All-uppercase strings like CBOR or ALL_CAPS now have a path through Str::studly() and Str::pascal() that produces the expected output. Pass normalize: true and any fully-uppercased word segment gets lowercased before conversion - mixed-case strings are left alone as before.
Str::studly('CBOR', normalize: true) // 'Cbor'
Str::studly('ALL_CAPS', normalize: true) // 'AllCaps'
Str::studly('AllJersey', normalize: true) // 'AllJersey' (unchanged)Add Client\Request::uri()
Pull request by @stevebauman
The HTTP client's Request object now has a uri() method that returns an Illuminate\Support\Uri instance, consistent with the uri() method already available on Illuminate\Http\Request. This makes Http::assertSent() callbacks a lot cleaner when you need to inspect the URL.
Http::assertSent(function (Request $request) {
return $request->uri()->path() === '/api/users'
&& $request->uri()->query()->integer('page') === 2;
});Inertia
Support Anchor Target Attribute
Pull request by @devhammed
The <Link> component now accepts a target attribute, so you can open links in a new tab with target="_blank" without reaching for a plain <a> tag. A small quality-of-life improvement that brings the component in line with standard HTML and popular router libraries.
Add Page to titleCallback
Pull request by @mrleblanc101
The titleCallback function now receives the full current page object as a second argument, giving you access to page props and metadata when constructing the document title. Useful when you want to build a title from data that lives on the page rather than just the title string itself.
Add Async Option to FormComponentOptions
Pull request by @sawirricardo
The async visit option was silently dropped when passed to a <Form> component, meaning options={{ async: true }} had no effect and forms always submitted synchronously. The option is now properly included in FormComponentOptions and passed through to the underlying visit.
Await User-Provided Form onSuccess Callback Before Completing Submission
Pull request by @mattwigham
If your <Form> component's onSuccess callback returned a Promise - say, polling a background job before reloading - the form's processing state would clear before that async work finished. This made disableWhileProcessing unreliable and any UI tied to processing behave unexpectedly. The fix is simple: the callback is now properly awaited before the form marks itself as done.
Pass Props to withApp Callback
Pull request by @CL0Pinette
The withApp callback now receives the initial page props, giving you access to data like the user's locale or other bootstrapped values at app setup time. This restores a capability that existed in the older setup callback before withApp was introduced.
AI
Add MCP Client and Server Tool Support
Pull request by @pushpak1300
Laravel AI agents can now use MCP tools directly - both from remote MCP servers over HTTP and from local MCP server classes defined in your application. Pass them into an agent's tools() method and the framework handles schema translation, wrapping, and invocation automatically.
// Tools from a remote MCP server
public function tools(): array
{
return [
...Client::web('https://mcp.example.com')->withToken($token)->tools(),
new MyOwnTool,
];
}
// Reuse a local MCP server tool class as an agent tool
public function tools(): array
{
return [
new CurrentWeatherTool,
new MyOwnTool,
];
}Check out the Laravel AI documentation to see how agents and MCP tools fit together.
Bedrock Support for Remote S3 Document
Pull request by @dumbbellcode
When working with large documents in Bedrock, you no longer have to download files from S3 and re-upload them inline. The new S3Document type lets you reference an S3-hosted file by URI and Bedrock will fetch it directly, keeping large payloads out of your application's memory.
use Laravel\Ai\Files\S3Document;
$document = new S3Document('s3://my-bucket/reports/q1.pdf', mimeType: 'application/pdf');
ai('bedrock')->text('Summarize this report.', [$document]);Support OpenAI Zero Data Retention via Encrypted Reasoning
Pull request by @Junveloper
Organizations with Zero Data Retention enabled on OpenAI could complete the first AI turn fine, but the moment a tool ran and a follow-up request was needed, OpenAI would reject it because previous_response_id is forbidden under ZDR. Set OPENAI_ENCRYPTED_REASONING=true and Laravel AI switches to a fully stateless path - reasoning state is carried via OpenAI's encrypted content blobs instead of stored server-side, and the full conversation is sent inline on every request. No changes needed to your agent code.
Boost
Add MCP Server Icon Metadata
Pull request by @sneycampos
The Laravel Boost MCP server now includes icon metadata so MCP clients that support it - like the Codex App on macOS - can display a branded Boost icon in source and tool surfaces.
Add --dirty Flag for Faster Formatting
Pull request by @calebdw
Boost's Pint integration now runs with --dirty by default, so only files that have actually changed get formatted. Combined with --parallel, formatting is significantly faster in agentic workflows where only a handful of files are touched per turn.
Make MCP Tool Timeout Configurable
Pull request by @jstar0
The internal MCP tool subprocess timeout is now configurable via the BOOST_MCP_TOOL_TIMEOUT environment variable, with a boost.mcp.tool_timeout config key to match. If you're hitting the previous 180-second hardcoded limit on slow tools, you can now raise it without touching the source.
Add Pi Agent Support
Pull request by @cyrodjohn
Pi is now a supported Boost agent. Boost detects Pi from the pi binary and .pi/settings.json config and installs guidelines and skills in the right places for the Pi workflow.
Feat: Adds Google Antigravity Agent
Pull request by @achyutkneupane
Google Antigravity joins the list of supported Boost agents. Now that Antigravity v2 supports workspace-scoped MCP, Boost can install guidelines, skills, and MCP configuration directly into your project for it.
Add Factory Droid Agent
Pull request by @travisobregon
Factory Droid is now a supported Boost agent. Boost detects it via the droid CLI and .factory project directory, and sets up guidelines, skills, and MCP configuration automatically.
Add Zed Editor Agent
Pull request by @pushpak1300
Zed joins the Boost agent ecosystem. Boost detects Zed on macOS via /Applications/Zed.app (and via zed in PATH elsewhere), installs guidelines to AGENTS.md, skills to .agents/skills, and writes MCP server configuration to .zed/settings.json under context_servers.
Echo
Add useSocketId() Hook to React, Vue and Svelte Adapters
Pull request by @hectorgrecco
All three Echo framework adapters now ship a useSocketId() reactive hook that gives you the current socket ID and automatically updates when the connection reconnects with a new one. Pass it in an X-Socket-Id header to prevent your own broadcast events from being received back.
// React
import { useSocketId } from "@laravel/echo-react";
const socketId = useSocketId();
// Vue
import { useSocketId } from "@laravel/echo-vue";
const socketId = useSocketId(); // ComputedRef<string | undefined>
// Svelte
import { useSocketId } from "@laravel/echo-svelte";
const socketId = useSocketId(); // getter functionStarter Kits
Guess Passkey Name From User Agent in Livewire Starter Kits
Pull request by @luisprmat
Livewire starter kits now guess a passkey name from the user agent string when registering a new passkey, matching behavior that was already present in the Inertia starter kits. Users see a friendlier, device-specific name rather than a generic placeholder.
Add Leave Team Button to Teams Kits
Pull request by @WendellAdriel
Team members can now leave non-personal teams directly from the teams list page, without needing an owner to remove them. A confirmation modal prevents accidents, and if you leave the team you're currently on, you're automatically switched to your next available team.
Advertise Passkey Endpoints via the Well-Known Url
Pull request by @benbjurstrom
Starter kits now expose /.well-known/passkey-endpoints as required by the W3C specification - a prerequisite for Apple devices to properly discover and use passkeys on your application. The endpoint is only included when the passkeys feature is enabled during installation.
Re-Add Blaze to Livewire Starter Kits
Pull request by @WendellAdriel
livewire/blaze is back in the Livewire starter kit composer manifests. Newly generated Livewire applications will have the package available as expected.
Add Larastan to Starter Kits
Pull request by @WendellAdriel
Larastan at level 7 is now included in both Livewire and Inertia starter kits. Static analysis runs as part of the test script and CI workflows, and the starter kit code has been updated with the necessary types and annotations to pass cleanly at that level.
Teams Invitation Flow Improvements
Pull request by @WendellAdriel
The team invitation experience got a polish pass. Clicking an invite link in an email now shows a contextual banner on the login and register pages so users understand why they're there. After logging in, any pending invitations surface in a modal on the dashboard - accept or decline without hunting for a settings page.
MCP
Add Resources Support to the MCP Client
Pull request by @pushpak1300
The MCP client can now list and read resources from any connected server, joining tools and prompts as first-class operations. Use $client->resources() to get a paginated collection keyed by URI, and $client->readResource() to fetch a specific resource by its URI.
$client = Mcp::client('everything')->connect();
$resources = $client->resources();
$result = $client->readResource('file://readme');
$result->content(); // the file contents
$result->mimeType(); // "text/plain", etc.Add MCP Client Tool Listing and Calling
Pull request by @pushpak1300
The MCP client can now discover and call tools on any connected server. $client->tools() returns an auto-paginated collection keyed by name - iterate it to see what's available, or call any tool directly with $client->callTool().
$client = Client::local('npx', ['-y', '@modelcontextprotocol/server-everything']);
$tools = $client->tools();
$tools->keys()->all(); // ['add', 'printEnv', ...]
$result = $client->callTool('add', ['a' => 1, 'b' => 2]);
$result->text(); // "3"See the MCP documentation for the full client API.
Add MCP Client Streamable Http Transport
Pull request by @pushpak1300
The MCP client now supports the Streamable HTTP transport from the MCP 2025-11-25 spec, making it possible to connect to remote MCP servers over HTTP alongside the existing stdio transport. Bearer token authentication is built in.
// Remote server over HTTP
$client = Client::web('https://mcp.example.com/mcp');
// With a bearer token
Client::web('https://mcp.example.com/mcp')->withToken(env('REMOTE_TOKEN'));HTTP-based clients are fully testable via Http::fake(), just like any other HTTP call in Laravel.
Add Prompts Support to the MCP Client
Pull request by @pushpak1300
Prompt listing and retrieval round out the MCP client's protocol support. $client->prompts() returns a collection of available prompts keyed by name, and $client->getPrompt() fetches a specific prompt with any required arguments.
$client->prompts(); // Collection<string, Prompt>
$client->getPrompt('greeting', ['name' => 'John'])->text();Prompts
Add Callout Component
Pull request by @joetannenbaum
The callout() function adds styled informational boxes to CLI output - handy for highlighting warnings, surfacing key information, or drawing attention to something important without just dumping text. Supports headings, bulleted and numbered lists, key-value pairs, inline code formatting, warning and error variants, and an optional footer.
Print Completion Line After Task Finishes
Pull request by @lazerg
Previously, when a task() finished, the spinner and label would disappear from the terminal with nothing to show for it. Now a checkmark + label line is printed after the spinner clears, so users can see which tasks have completed at a glance.
Sail
Add PHP_EXTENSIONS Build Arg to Install Additional PHP Extensions
Pull request by @sydgren
Installing extra PHP extensions in Sail no longer requires publishing and hand-editing the Dockerfile. Pass a PHP_EXTENSIONS build argument in docker-compose.yml with a space-separated list of extensions, and Sail's Dockerfile will install them at build time.
services:
laravel.test:
build:
context: ./vendor/laravel/sail/runtimes/8.4
dockerfile: Dockerfile
args:
WWWGROUP: "${WWWGROUP}"
PHP_EXTENSIONS: "gmp imagick"Existing builds are unaffected - the argument defaults to empty.
Scout
Add Opt-in Unique Indexing Jobs to Prevent Scout Reindexing Already Queued Models
Pull request by @stevebauman
If the same model gets saved multiple times in quick succession, Scout can end up queuing redundant indexing jobs for it. The new unique job variants - MakeSearchableUnique and RemoveFromSearchUnique - implement ShouldBeUniqueUntilProcessing, so only one job per model sits in the queue at a time. Opt in by registering them in a service provider.
use Laravel\Scout\Scout;
use Laravel\Scout\Jobs\MakeSearchableUnique;
use Laravel\Scout\Jobs\RemoveFromSearchUnique;
Scout::makeSearchableUsing(MakeSearchableUnique::class);
Scout::removeFromSearchUsing(RemoveFromSearchUnique::class);These require a default cache store, so they're opt-in rather than the new default.
Socialite
Add Ability to Easily Fake Socialite Users
Pull request by @stevebauman
Setting up a fake Socialite user in tests used to require constructing and mapping an attribute array by hand before passing it to Socialite::fake(). The new User::fake() static method takes care of all of that - pass only the attributes you care about and sensible defaults fill in the rest.
// Before
Socialite::fake('github', (new User)->setRaw($attributes)->map($attributes)
->setToken('fake-token')
->setRefreshToken('fake-refresh-token')
->setExpiresIn(3600)
->setApprovedScopes([]));
// After
Socialite::fake('github', User::fake([
'email' => 'test@example.com',
]));Works for OAuth 1 users too, via \Laravel\Socialite\One\User::fake().