News

Laravel 12.51.0 Adds afterSending Callbacks, Validator whenFails, and MySQL Timeout

Published
Laravel 12.51.0 Adds afterSending Callbacks, Validator whenFails, and MySQL Timeout image

Release updates

Never miss a Laravel release

Get an email whenever a new Laravel release lands.

Release Date: February 10, 2026
Laravel Version: 12.51.0

Summary

Laravel v12.51.0 adds notification afterSending() callbacks, fluent whenFails() and whenPasses() methods on the Validator, a MySQL query builder timeout() method, and closure support in firstOrCreate and createOrFirst for lazy value evaluation. This release also introduces a BatchCancelled event, support for Eloquent builders as subqueries in update queries, and a withoutHeader() method on responses.

Key highlights include:

  • Notification afterSending() callbacks
  • Validator whenFails() and whenPasses() methods
  • MySQL query builder timeout() method
  • Closure support in firstOrCreate / createOrFirst
  • BatchCancelled event
  • Eloquent builders as subqueries in update queries
  • withoutHeader() on Response
  • Batch testing improvements and cache isolation for parallel tests
  • Numerous bug fixes and type improvements

What's New

Notification afterSending() Callbacks

Notification classes can now define an afterSending method that runs after the notification is sent on each channel. This provides a convenient way to handle post-send logic — like updating a model or firing an event — without registering a dedicated NotificationSent listener:

class BookingNotification extends Notification
{
public function __construct(public Booking $booking) {}
 
public function via(): array
{
return ['mail'];
}
 
public function toMail(): MailMessage
{
// ...
}
 
public function afterSending($notifiable, $channel, $response)
{
$this->booking->update(['notified_at' => now()]);
}
}

The method receives the notifiable instance, the channel name, and the channel's response, giving you full context for any follow-up actions.

Pull Request: #58654

Validator whenFails() and whenPasses() Methods

The Validator now includes fluent whenFails() and whenPasses() methods for handling validation results, which is particularly useful outside the HTTP request cycle — such as in Artisan commands or queue jobs:

public function someMethod($file)
{
Validator::make(
['file' => $file],
['file' => 'required|image|dimensions:min_width=100,min_height=200']
)->whenFails(function () {
throw new InvalidArgumentException('Provided file is invalid');
});
}

These methods provide an alternative to manually checking $validator->fails() or wrapping validation in try/catch blocks.

Pull Request: #58655

MySQL Query Builder timeout() Method

A new timeout() method on the query builder sets a per-query execution timeout for MySQL using the MAX_EXECUTION_TIME optimizer hint:

Student::query()
->where('email', 'like', '%text%')
->timeout(60)
->get();
 
// Generates: select /*+ MAX_EXECUTION_TIME(60000) */ * from `students` where `email` like ?

You can also apply it as a default via a global scope:

use Illuminate\Database\Eloquent\Attributes\ScopedBy;
 
class TimeoutScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->timeout(60);
}
}
 
#[ScopedBy([TimeoutScope::class])]
class User extends Model
{
// ...
}

This is MySQL-specific and accepts a timeout value in seconds.

Pull Request: #58644

Closures in firstOrCreate and createOrFirst

The firstOrCreate and createOrFirst methods now accept closures for the $values parameter, enabling lazy evaluation of expensive operations:

// Before — expensive operation always runs
$location = Location::query()->firstWhere('address', $address);
 
if ($location) {
return $location;
}
 
return Location::create([
'address' => $address,
'coordinates' => Geocoder::resolve($address),
]);
 
// After — geocoding only runs when a new record is needed
$location = Location::firstOrCreate(
['address' => $address],
fn () => ['coordinates' => Geocoder::resolve($address)],
);

When the record already exists, the closure is never evaluated, avoiding unnecessary API calls, computations, or other expensive work.

Pull Request: #58639

BatchCancelled Event

A new BatchCancelled event fires globally when a batch is cancelled, whether automatically due to a job failure or through a manual cancellation call. This lets you listen for batch cancellations across your application without polling:

use Illuminate\Bus\Events\BatchCancelled;
use Illuminate\Support\Facades\Event;
 
Event::listen(BatchCancelled::class, function (BatchCancelled $event) {
Log::warning("Batch {$event->batch->id} was cancelled.");
});

Pull Request: #58627

Eloquent Builders as Subqueries in Updates

Eloquent builders and relations can now be used directly as subqueries in update statements without converting to a base query:

// Before
FooModel::where('...')->update([
'bar_id' => BarModel::where('...')->toBase()->select('id'),
]);
 
// After
FooModel::where('...')->update([
'bar_id' => BarModel::where('...')->select('id'),
]);

The ->toBase() call is no longer required when passing an Eloquent builder as a subquery value.

Pull Request: #58692

withoutHeader() on Response

A new withoutHeader() method allows you to remove headers from HTTP responses, providing symmetry with the existing withoutCookie() method:

// Remove a single header
return response($content)->withoutHeader('X-Debug');
 
// Remove multiple headers
return response($content)->withoutHeader(['X-Debug', 'X-Powered-By', 'Server']);

Pull Request: #58671

Bug Fixes and Improvements

Testing:

  • assertJobs method on PendingBatchFake for batch job assertions (#58606)
  • Bus::assertBatched() with array support (#58659)
  • viewData() without key returns all view data on TestResponse (#58700)
  • Cache prefix isolation for parallel testing via TestCaches trait (#58691)

HTTP Client:

  • throwIfStatus/throwUnlessStatus now works for all status codes including 2xx and 3xx (#58724)

Database & Eloquent:

  • orderByPivotDesc() method for descending pivot column ordering (#58720)
  • whereBetween accepts DatePeriod and handles missing end dates (#58687)
  • SSL cert/key support for MySQL schema dump and load (#58690)
  • Fix Postgres sequence starting value for custom schemas/connections (#58199)
  • Adjust freshTimestamp for SQL Server (#58614)
  • Fix batch counts when deleteWhenMissingModels skips missing model jobs (#58541)

Strings & Helpers:

  • Stringable::deduplicate() accepts array of characters (#58649)
  • Fix Str::substrReplace for edge cases with negative offset or length (#58634)
  • Fix Str::isUrl() returning false for single-character domain names (#58686)
  • Replace substr with mb_substr for user agent encoding (#58703)

Queue & Middleware:

  • Allow specifying Redis connection on Redis-based queue middleware (#58656)
  • Fix Queue::fake() not releasing unique job locks between tests (#58718)

Translation & Localization:

  • Prevent duplicate locale checks in Lang::get() when locale matches fallback (#58626)
  • Fix trans_choice regex to allow negative ranges (#58648)

Framework & Internals:

  • Restore original dispatcher bindings after precognitive request — fixes Octane and Pest (#58716)
  • Handle binary data in Js::encode() debug renderer (#58618)
  • Add ArrayObject props to AsEncryptedArrayObject to match AsArrayObject (#58619)
  • Fix exception page pop-in for non-main frames (#58698)
  • Fix Laravel ASCII SVG character alignment (#58702, #58719)
  • Add deprecation to Request::get() (#58635)
  • Update reload tasks to include schedule:interruption (#58637)

References

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

laravelcloud logo
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud

The latest

View all →
CPX: The Composer Package Executor for PHP image

CPX: The Composer Package Executor for PHP

Read article
Laravel AI SDK Adds Human-in-the-Loop Tool Approval image

Laravel AI SDK Adds Human-in-the-Loop Tool Approval

Read article
Pest 5 Released With Test Impact Analysis, Agent Verification, and Evals image

Pest 5 Released With Test Impact Analysis, Agent Verification, and Evals

Read article
Queue-SQL: Run Mass Deletes and Updates Across Parallel Queue Jobs image

Queue-SQL: Run Mass Deletes and Updates Across Parallel Queue Jobs

Read article
Blade Formatting in Laravel Pint image

Blade Formatting in Laravel Pint

Read article
Inertia DevTools Is Now on the Chrome Web Store image

Inertia DevTools Is Now on the Chrome Web Store

Read article