To use this site please enable javascript on your browser! August 2021 Laravel Updates v8.53 - v8.58

We use cookies, as well as those from third parties, for sessions in order to make the navigation of our website easy and safe for our users. We also use cookies to obtain statistical data about the navigation of the users.

See Terms & Conditions

August 2021 Laravel Updates v8.53 - v8.58

by Bryce van Andy 11:08 Aug 31 '21

Here is what the Laravel team and community have been cooking for August 2021:

Laravel Logo

Version 8.53

  • queue:monitor command

Laravel team member @themsaid added another brilliant feature within queues that displays the sizes of the given queues, their connections and status.

Queue monitor screenshot

The best part of this feature is the ability to monitor when the queues are overwhelmed with jobs or when a certain limit of jobs is exceeded.

Queue monitor limit screenshot

Once any queue is on ALERT, Laravel will fire the QueueBusy event and you can handle this as you wish, maybe by sending an email, notification or much better by increasing your queue workers to process excess jobs.

  • twiceDailyAt schedule frequency

The task scheduler has a new schedule frequency, thanks to @Doug_Sisk. Now you can schedule a task to run twice in a day at specific minutes of the hour.

$schedule->job(...)->twiceDailyAt(1, 13, 30);

The job above will be run at 01:30 and 13:30, the first two arguments are the hours and the third is the minutes offset.

  • Immutable date & datetime casting

We now have support for immutable dates, a feature added by @hotmeteor.

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'verified_at' => 'immutable_date',
    'created_at' => 'immutable_datetime:Y-m-d',
];

This reduces a lot of bugs associated with having different values for dates when they have interacted with various Carbon methods, thus adding the much needed consistency.

  • accepted_if validation rule

Another form validation rule based on the accepted rule, the only difference is it depends on another field value. Thanks to @Khan 's contribution.

Version 8.54

  • Support for GCM encryption

The encryption cipher supported prior to this update was CBC, now we have support to use GCM, thanks to @nickakitch. To make this switch, you will need to update the cipher key of the config/app.php file:

'cipher' => 'AES-256-GCM', // Used to be 'AES-256-CBC'

And don't forget to decrypt all encrypted data in the app with the old cipher and then encrypt with the new. GCM allows for more secure encryption and better performance.

  • Exception parameter in the route's missing() method

An update from @Anders sees the possibility to handle missing models differently depending on the exception thrown, thats why he added the $exception as another argument:

Route::get('/locations/{location:slug}/photos/{photo}', [LocationsController::class, 'show'])
    ->name('locations.photos')
    ->missing(function ($request, $exception) {
        if ($exception->getModel() == 'App\Models\Location') {
            return Redirect::route('locations.index', null, 301); // Only redirect user if the location is not found
        }

        // For all other models simply render the ModelNotFoundException as we would normally do
    );
  • attempt() method on rate limiter

@Italo has made a significant improvement on the rate limiter feature. Previously, you would need to do the following to check whether a limit has reached:

use Illuminate\Support\Facades\RateLimiter;

if (RateLimiter::tooManyAttempts('something', 5)) {
    return false;
}

// Do something...

RateLimiter::hit('something', 60);

return true;

But now with the new attempt method, you can do the above with a one liner. It will return false if the limit is reached and true or the result of the callback if otherwise:

return RateLimiter::attempt('something', 5, function () {
    // Do something...
});
  • withoutTrashed() on exists rule

Lastly from @Sagar, we now have a withoutTrashed method on the exists validation rule. Instead of writing this:

Rule::exists('users')->whereNull('deleted_at')

We can now write:

Rule::exists('users')->withoutTrashed()

Version 8.55

  • stringable support for isUuid method

@Andrew added readability around the use of isUuid method by making it stringable:

// Before
Str::isUuid(Str::of($filename)->after('-')->before('.'));
// After
Str::of($filename)->after('-')->before('.')->isUuid();
  • withTrashed allowing deleted models on routes

Creator of Laravel @Taylor, added the ability to allow processing of deleted models instead of directly throwing a 401

Route::get('/user/{user}', function (User $user) {
    // Do something with $user;
})->withTrashed();
  • Mail failover support

Thanks to @László now there is a new mail driver failover, which decides the order in which the next driver will pick up from a failed driver

'mailers' => [
        'failover' => [
            'transport' => 'failover',
            'mailers' => [
                'mailgun',
                'postmark',
                'sendmail',
            ],
        ],

        'mailgun' => [
            'transport' => 'mailgun',
        ],

        'postmark' => [
            'transport' => 'postmark',
        ],
        //
    ],

From the above mail configuration, whenever sending emails fails using mailgun, the failover driver will try again with the postmark driver, and if that fails it will send it with the sendmail driver until one succeeds or all fail.

  • Conditional form request rules

Another addition by @Taylor, now you can conditionally add validation rules. This one of my favourite features as I can see many of my form requests being cleaned up

[
    'name' => 'required|string|min:6',
    'password' => ['required', 'string', Rule::when(someBooleanCondition, ['min:5', 'confirmed'])],
]
  • Testing assertion on redirecting to signed routes

The new assertion assertRedirectToSignedRoute will now assert if there is a redirection to a route, thanks to @AshAllen

$this->get(route('laravel.index'))
        ->assertRedirectToSignedRoute();

// With the route and params
$this->get(route('laravel.index'))
    ->assertRedirectToSignedRoute('example.route', ['param' => 'hello']);
  • Fetching validated subsets

@Taylor added another feature where we can ask the validator only for a subset of the validated data:

$validator->safe()->only(['name', 'email']);
$validator->safe()->except([...]);

$formRequest->safe()->only([...]);
$formRequest->safe()->except([...]);
  • Simplified assertion on JSON and session errors

Again from @Taylor, we have a simplified uniform assetion of errors for both JSON and session data:

$response->assertValid(['address']);

$response->assertInvalid(['name', 'email']);

$response->assertInvalid([
    'name' => 'required',
]);
  • qualifyColumns method on models

@Gianluca's contribution now will prevent using multiple qualifyColumn to using a single qualifyColumns:

// Before
$query->select( $model->qualifyColumn('name'), $model->qualifyColumn('surname'), $model->qualifyColumn('address'));
// After
$query->select($model->qualifyColumns(['name', 'surname', 'address']));
  • Custom validation exceptions

@SteveBauman contributed to having custom validation exceptions:

class AppValidationException extends \Illuminate\Validation\ValidationException {}

// Somewhere in the application...

$validator = Validator::make($data, $rules, $messages);

$validator->setException(AppValidationException::class);

$validator->validate();

Version 8.56

  • firstOrFail in collections and lazy collections

With this addition it is possible to chain the firstOrFail method in collections, thanks to @Powell:

$collection->where('name', 'fish')->firstOrFail();

This will then throw an exception once an item is not found.

  • Default rule in conditional rules

@Bastien added the possibility to define default rules for conditional rules, so now you can have

'email' => [
    Rule::when($this->someComplexTest(), ['required'], ['nullable']),
]

That's if someComplexTest is falsy, then the required rule will be skipped and only nullable takes effect.

  • fullUrlWithoutQuery method added to Request

@AliSaleem added simple method to easily remove query parameters from a URL:

//  https://example.com?color=red&shape=square&size=small

request()->fullUrlWithoutQuery('color');
// https://example.com?shape=square&size=small

request()->fullUrlWithoutQuery(['color', 'size']);
// https://example.com?shape=square

Version 8.57

  • exclude validation rule

Again @Bastien contributed with the exclude rule, where the field under validation will be excluded from the request data after validation

  • $when callback inside Http client retry method

Thanks to @DanielMason we now have a when callback in the Http::retry(), similar to that in the retry helper, the callback determines if the retries should be attempted:

$response = Http::retry(3, 100, function ($exception) {
    return $exception instanceof ConnectionException;
})->post(...);
  • assertUnprocessable() when testing invalid data responses

@Joby added a much needed status code assertion when the response has validation errors.

Simply use assertUnprocessable() as it works similarly to other status code assertions such as assertOk() or assertUnauthorized().

public function test_password_is_required_to_login()
{
    $this->post(route('login'), ['username' => 'joe'])
        ->assertUnprocessable();

    $this->post(route('login'), ['username' => 'joe', 'password' => 'userpass'])
        ->assertRedirect(route('dashboard'));
}
  • where helper for querying relations

Another big contribution from @Italo is the where helper for querying relations. This will enable us to shorten:

// This:
User::whereHas('posts', function ($query) {
    $query->where('published_at', '>', now());
})->get();

// Into this:
User::whereRelation('posts', 'published_at', '>', now())->get();

Another complex example from one of my projects I could see myself refactoring:

// Turning this
$transactions = Transaction::with(['transactionable.user'])
    ->orderByDesc('created_at')
    ->whereHasMorph('transactionable', ['App\Models\Order'], function (Builder $query) {
        $query->where('user_id', '=' , auth()->id());
    })
    ->take(request('limit') ?? 20)
    ->get();

// Into this
$transactions = Transaction::with(['transactionable.user'])
    ->orderByDesc('created_at')
    ->whereMorphRelation('transactionable', ['App\Models\Order'], [
        'user_id' => auth()->id(),
    ])
    ->take(request('limit') ?? 20)
    ->get();

Version 8.58

  • updateOrFail on models

@Claas added a model method to conveniently handle ->update() errors. Instead of checking the actual result of ->update(), you can use the new method to immediately fail if necessary.

  • prohibits validation rule

Finally, an important addition by @Samlev, is the new prohibits validation rule.

Validator::validate([
    'post_id' => 1, 
    'created_by' => 2,
    'created_at' => '2020-07-16 20:54:22'
], [
    'post_id' => 'prohibits:created_by,created_at',
]);

This simply means, if the post_id field exists then it cannot co-exist with the other fields created_at and created_by.

That's all folks!

If you like this content, please consider buying me coffee.
Thank you for your support!

Become a Patron!