News

Rule Builders for Every Validation Rule in Laravel 13.36

Published
Rule Builders for Every Validation Rule in Laravel 13.36 image

The Laravel team released v13.36.0 with Rule:: builders for the validation rules that only had string syntax, DB::olap() for large queries on PlanetScale and other Vitess databases, an opt-in reset of sticky database connections between queue jobs, synchronous Mercure broadcasts by default, and more.

Rule Builders for the Remaining Validation Rules

@ericmp33 added a Rule:: method for every validation rule that did not have one. Rules like required_with, accepted_if, url, uuid, and distinct can now be written with methods your IDE can autocomplete:

use Illuminate\Validation\Rule;
 
$request->validate([
'phone' => [Rule::requiredWithout('email')],
'website' => ['nullable', Rule::url()->protocols(['https'])],
'device_id' => [Rule::uuid()->version(4)],
'tags.*' => [Rule::distinct()->ignoreCase()],
'password' => ['required', Rule::confirmed()],
]);

Rules without parameters, such as Rule::required() and Rule::nullable(), return the plain rule string. Rules that take field names or values return an object that passes its parameters to the validator as an array, so a value containing a comma is no longer split into two values. The PR also adds equals() to the fluent Rule::date() builder.

See #61833 for more details.

DB::olap() for PlanetScale and Vitess

Jack Bayliss added olap() and isVitess() to the MySQL connection. PlanetScale runs on Vitess, which limits queries in its default OLTP workload. On PlanetScale, one query can return at most 100,000 rows. The olap() method switches to the OLAP workload for the length of a callback and switches back when it returns:

$revenue = DB::olap(fn () => Order::query()
->selectRaw('customer_id, sum(total) as revenue')
->where('created_at', '>=', now()->subYears(3))
->groupBy('customer_id')
->get()
);

It sets the workload on both the read and write connections. On a database that is not Vitess, it only runs the callback, so the same code works against a local MySQL server.

See #61932 for more details.

Reset Sticky Connections Between Queue Jobs

With sticky enabled on a read/write database connection, a write sends later reads to the write connection. A queue worker is one long-running process, so a single job that writes can send the reads of every later job in that worker to the write connection. Jack Bayliss added an opt-in flag that resets the state before each job:

use Illuminate\Queue\Worker;
 
// AppServiceProvider::boot()
Worker::$flushStickyConnections = true;

It is off by default, because a job could depend on reading what an earlier job in the same worker wrote.

See #61915 for more details.

Mercure Broadcasts Run Synchronously by Default

Kévin Dunglas changed the Mercure broadcaster to publish events right away instead of queueing them. Events that only target Mercure connections now behave like ShouldBroadcastNow, and events that also target another connection are still queued. Set MERCURE_ASYNC=true to queue them again. Async mode needs a Mercure url, since queue workers publish over HTTP:

MERCURE_URL="${APP_URL}/.well-known/mercure"
MERCURE_ASYNC=true

The same PR fixes Artisan commands crashing with BROADCAST_CONNECTION=mercure when no url is set and the FrankenPHP mercure_publish() function is not available.

See #61977 for more details.

DeleteWhenMissingModels and FailOnTimeout on Queued Mailables

Volodya Kurshudyan fixed queued mailables ignoring the #[DeleteWhenMissingModels] and #[FailOnTimeout] attributes and their matching properties:

#[DeleteWhenMissingModels]
class OrderShipped extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
 
public function __construct(public Order $order) {}
}

If the order is deleted before the mail is sent, the job is now deleted instead of failing with a ModelNotFoundException. With #[FailOnTimeout], a mail that times out is marked as failed instead of being retried and possibly sent twice.

See #61899 for more details.

Escaping in Markdown Mail

Stephen Rees-Carter fixed the escaping of user input in Markdown mailables. A value placed in the text of a Markdown link could use a backslash to escape the closing bracket and change the link target. Backslashes are now escaped along with the brackets.

See #61755 for more details.

Release Notes

You can see the complete list of new features and updates below and the diff between 13.35.0 and 13.36.0 on GitHub. The following release notes are directly from the changelog:

v13.36.0

  • [12.x] Make the worker timed out exit code reachable by @kieranbrown in #61623
  • [12.x] Support named S3 credential providers and Cloud IAM disks by @kieranbrown in #61757
  • [12.x] Address query issues by @taylorotwell in #61838
  • [12.x] Backport EncodedParameter from #61610 by @mahdyaralipor in #61871
  • [12.x] Generate different cache file based on configured $echoFormat by @crynobone in #61822
  • [13.x] Add background connection to the default queue config by @svidskiy in #61917
  • [13.x] Allow queue workers to flush sticky connections between jobs by @jackbayliss in #61915
  • [13.x] Allow null in TestResponse::$streamedContent docblock by @alies-dev in #61914
  • [13.x] Support DeleteWhenMissingModels and FailOnTimeout on queued mailables by @xurshudyan in #61899
  • [13.x] Rebuild the phpredis client when no cluster node is reachable by @cwang22 in #61911
  • [13.x] Include connection in queue:work --json paused and resumed output by @jackbayliss in #61896
  • Fix memory leak in test suites caused by Prompts by @timacdonald in #61872
  • [13.x] Fix Notification::locale() being ignored after a notification was sent by @Taldres in #61904
  • [13.x] Resolve morph map aliases in constrain(), morphWith() and loadMorph() by @xurshudyan in #61898
  • [13.x] Recognize PgBouncer SSL EOF failover errors as lost connections by @0x15f in #61918
  • [13.x] Include queue and connection in queue:work --json stop output by @jackbayliss in #61858
  • [13.x] Keep the console kernel's own commands on the kernel instance by @bert-w in #61931
  • [13.x] Preserve numeric keys in JSON translation files loaded by FileLoader by @sajjadhossainshohag in #61925
  • Fix Number::pairs() dropping the upper bound when it starts a new pair by @abdulmajidcse in #61921
  • [13.x] Fix failover and roundrobin mailers ignoring retry_after by @xurshudyan in #61927
  • [13.x] Fix eventStream() messages containing line breaks by @sajjadhossainshohag in #61923
  • Prevent escaping from bypassing protection by @valorin in #61755
  • [13.x] add IsVitess() and olap() to MySqlConnection by @jackbayliss in #61932
  • [13.x] Add validation rule builders for remaining validation rules by @ericmp33 in #61833
  • [13.x] Release persistent phpredis socket before rebuilding client by @0x15f in #61919
  • [13.x] Supports phpstan/phpstan:2.3.1 by @SanderMuller in #61939
  • [13.x] Fix touch() query generation on HasOneOrManyThrough relations by @HamidRezaLS in #61942
  • [13.x] Fix reference cycle from MorphTo soft delete constraints by @babrmonta in #61944
  • [13.x] Adjust Worker to not call the killback twice by @jackbayliss in #61940
  • [13.x] Fix Context::pop() throwing the wrong exception for missing keys by @xurshudyan in #61936
  • [13.x] Improve BelongsToMany collection return types by @axlon in #61954
  • [13.x] Make Schema::getColumn() case insensitive by @svidskiy in #61967
  • [13.x] Fix JSON path updates being lost after reading an Attribute accessor by @xurshudyan in #61962
  • [13.x] Fix Notification::locale() being applied to later notifications by @sumaiazaman in #61959
  • [13.x] Restore short-circuiting in Arr::first() for iterables by @DirkHoffman in #61957
  • [13.x] Batch Redis queue notification pushes by @hectorgrecco in #61965
  • [13.x] Repeat less in setTrustedProxyIpAddresses() by @weshooper in #61963
  • Apply fixes from StyleCI by @taylorotwell in #61969
  • [13.x] Remove duplicate and unused code from the View tests by @xurshudyan in #61976
  • [13.x] Fix scheduled callbacks rethrowing a previous exception on later runs by @sumaiazaman in #61973
  • [13.x] Fix unique queued listener locks not being released when the transaction is rolled back by @sumaiazaman in #61974
  • Bump the github-actions group with 3 updates by @dependabot in #61990
  • [13.x] Adjust UniqueJobTest by @jackbayliss in #61985
  • [13.x] fix(broadcasting): recognize UniqueFor attributes on events by @calebdw in #61981
  • [13.x] Fix sub-second process timeouts given as a CarbonInterval by @xurshudyan in #61979
  • [13.x] Fix the FrankenPHP Mercure hub in the CLI and broadcast Mercure events synchronously by default by @dunglas in #61977
  • [13.x] Prevent recursive reconnects while a connection is reconnecting by @virtualtear in #61995
  • [13.x] Skip contextual binding lookup when none are registered by @xurshudyan in #61993
  • [13.x] Fix extra attributes skipping casts when incrementing or decrementing a model by @sumaiazaman in #61991
Paul Redmond photo

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

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
PHP News: A New Site for PHP Releases, RFCs, and Packages image

PHP News: A New Site for PHP Releases, RFCs, and Packages

Read article
Pause Polling in Background Tabs in Inertia.js v3.9 image

Pause Polling in Background Tabs in Inertia.js v3.9

Read article
Look What You Helped Me Build image

Look What You Helped Me Build

Read article
BashBox: A Sandboxed Bash Interpreter for AI Agents in PHP image

BashBox: A Sandboxed Bash Interpreter for AI Agents in PHP

Read article
Running Laravel's Scheduler on Multiple Servers image

Running Laravel's Scheduler on Multiple Servers

Read article
Laravel Fake Assertions Now Accept Property Arrays image

Laravel Fake Assertions Now Accept Property Arrays

Read article