Laravel 9.31 Released

Laravel 9.31 Released

Laravel 9.31 released with added unique deferrable constants for PostgreSQL, Request lifecycle duration handler, Model::withoutTimestamps(…) method, Vite manifestHash function, DB connection to accept object and class doctrine types, Jobs Fake Batches, Model::getAppends(), Str::wrap() method, Make Vite macroable, and more including the fixes. Let’s check these updates in detail here:

1. Unique Keys deferrable constants for PostgreSQL:

Markus Koch contributed by adding unique deferrable initially deferred constants for PostgreSQL DB connection to make it possible to check a unique constraint only after updating. You have to add the following in your migration files.

Now you can also put a DEFERRABLE INITIALLY DEFERRED or a DEFERRABLE INITIALLY IMMEDIATE on unique keys.

DEFERRABLE INITIALLY DEFERRED:

$table->unique(['post_id', 'post_type', 'sequence'])
    ->deferrable()
    ->initiallyImmediate(false);

DEFERRABLE INITIALLY IMMEDIATE:

$table->unique(['post_id', 'post_type', 'sequence'])
    ->deferrable()
    ->initiallyImmediate();

Source File Edits: src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php

/**
 * Compile a unique key command.
 *
 * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
 * @param  \Illuminate\Support\Fluent  $command
 * @return string
 */
public function compileUnique(Blueprint $blueprint, Fluent $command)
{
    $sql = sprintf('alter table %s add constraint %s unique (%s)',
        $this->wrapTable($blueprint),
        $this->wrap($command->index),
        $this->columnize($command->columns)
    );

    if (! is_null($command->deferrable)) {
        $sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
    }

    if ($command->deferrable && ! is_null($command->initiallyImmediate)) {
        $sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
    }

    return $sql;
}

2. Request lifecycle duration handler:

Tim MacDonald contributed by adding a Request lifecycle duration handler that allows you to easily handle when an application request lifecycle exceeds a certain threshold and includes the duration of the request handle method and the terminating the access and processes of the application. The handler is invoked after the request has been returned to the user.

You can track your application request time duration and perform the required action if you want. In the below example, I have Log the information about the request handling if it take more than required time due to any situation either it is server load time, the API fetching request, the records from database using relations etc.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Carbon\CarbonInterval as Interval;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Support\Facades\Log;


class Dashboard extends Controller
{

    public function index(Request $request)
    {
        ...
        
        $this->app[Kernel::class]->whenRequestLifecycleIsLongerThan(
            Interval::seconds(1), // duration of the interval
            fn ($startedAt, $request, $response) => // perform the actions if time exceeds
                Log::notice('Request exceeded duration threshold.', [
                    'user' => $request->user()?->id,
                    'url' => $request->fullUrl(),
                    'duration' => $startedAt->floatDiffInSeconds(),
                ])
        );
    }

}

Source File Edits: src/Illuminate/Foundation/Http/Kernel.php

/**
 * Register a callback to be invoked when the requests lifecyle duration exceeds a given amount of time.
 *
 * @param  \DateTimeInterface|\Carbon\CarbonInterval|float|int  $threshold
 * @param  callable  $handler
 * @return void
 */
public function whenRequestLifecycleIsLongerThan($threshold, $handler)
{
    $threshold = $threshold instanceof DateTimeInterface
        ? $this->secondsUntil($threshold) * 1000
        : $threshold;

    $threshold = $threshold instanceof CarbonInterval
        ? $threshold->totalMilliseconds
        : $threshold;

    $this->requestLifecycleDurationHandlers[] = [
        'threshold' => $threshold,
        'handler' => $handler,
    ];
}

/**
 * When the request being handled started.
 *
 * @return \Illuminate\Support\Carbon|null
 */
public function requestStartedAt()
{
    return $this->requestStartedAt;
}

3. Model withoutTimestamps() method:

Tim MacDonald contributed by adding a static withoutTimestamps() for Eloquent which helps to prevent updating the updated_at column of the record.

$user = User::first();


// `updated_at` is not changed...

$user->withoutTimestamps(
    fn () => $user->update([
        'name'=>'bar',
        'reserved_at' => now()
    ])
);

Source File Edits: src/Illuminate/Database/Eloquent/Concerns/HasTimestamps.php

/**
 * Run the given callable without timestamping the model.
 *
 * @param  callable  $callback
 * @return mixed
 */
public function withoutTimestamps(callable $callback)
{
    if (! $this->usesTimestamps()) {
        return $callback();
    }

    $this->timestamps = false;

    return tap($callback(), fn () => $this->timestamps = true);
}

4. Vite manifestHash function:

Enzo Innocenzi contributed by adding a hash method to Illuminate\Foundation\Vite Class that returns a unique hash for the existed manifest exists and makes it useful to invalidate assets, for instance using Inertia.

class HandleInertiaRequests extends Middleware
{

    // ...

    public function version(Request $request): ?string
    {
        return Vite::hash(); // return the hashed string.
        return Vite::hash('title to hash'); // return the hashed string.
    }
}

Source File Edits: src/Illuminate/Foundation/Vite.php

/**
 * Get a unique hash or null if there is no manifest.
 *
 * @return string|null
 */
public function hash($buildDirectory = null)
{
    $buildDirectory ??= $this->buildDirectory;

    if ($this->isRunningHot()) {
        return null;
    }

    if (! is_file($path = $this->manifestPath($buildDirectory))) {
        return null;
    }

    return md5_file($path) ?: null;
}

5. PostgreSQL DB connection to accept object and class doctrine types:

Emílio B. Pedrollo contributed by managing the type of the argument in the registerDoctrineType function for postgresql connection. Creating enums with custom types using postgresql connection and registering them becomes easy with passing the Type object to registerDoctrineType we could just implement something like code below. it’s a niche thing but there is no downside to this implementation probably.

Connection::resolverFor('pgsql', function($connection, $database, $prefix, $config) {
  $connection = new PostgresConnection($connection, $database, $prefix, $config);
  
  foreach (self::$enums as $type) {
    $connection->registerDoctrineType((new class extends Type {
      protected $name;

      public function getSQLDeclaration(array $column, AbstractPlatform $platform)
      {
        return $this->name;
      }

      public function getName()
      {
        return $this->name;
      }

      public function setName($name): self
      {
        $this->name = $name;
        return $this;
      }
    })->setName($type), $type, 'string');
  }
    
  return $connection;
}

Source File Edits: src/Illuminate/Database/Connection.php

/**
 * Register a custom Doctrine mapping type.
 *
 * @param  Type|class-string<Type>  $class
 * @param  string  $name
 * @param  string  $type
 * @return void
 *
 * @throws \Doctrine\DBAL\DBALException
 * @throws \RuntimeException
 */
public function registerDoctrineType(Type|string $class, string $name, string $type): void
{
    if (! $this->isDoctrineAvailable()) {
        throw new RuntimeException(
            'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).'
        );
    }

    if (! Type::hasType($name)) {
        Type::getTypeRegistry()
            ->register($name, is_string($class) ? new $class() : $class);
    }

    $this->doctrineTypeMappings[$name] = $type;
}

6. Fake Batches

Taylor Otwell contributed by provided the jobs fake batch to test the application if the batch was canclled by the job or if a jobs added additional jobs to the batch. We can create a FakeBatch manually and override the cancel / add methods, etc.

[$job, $batch] = (new TestJob)->withFakeBatch();

$job->handle();

$this->assertTrue($batch->cancelled());
$this->assertNotEmpty($batch->added);

Source File Edits: src/Illuminate/Bus/Batchable.php

The source code of the file is edited almost complete code. So, provideing the entire code is not possible here. Please refer the source file link and find on github laravel repository here https://github.com/laravel/framework/blob/9.x/src/Illuminate/Bus/Batchable.php

7. Model getAppends() Method:

Arturo Rodríguez contributed by added the accessor method to Model to get the accessors that are being appended to model arrays to make it useful for custom model mappings to get the appended content of the model arrays.

// To Set the accessors to append to model arrays.  
$user->append('is_admin')->toArray();
 
$user->setAppends(['is_admin', 'camelCased', 'StudlyCased'])->toArray();


// Get the accessors to append to model arrays.
$user  = $user->getAppends();
$user->is_admin // return true;
$user->camelCased // return true;
$user->StudlyCased // return true;

Source File Edits: src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

/**
 * Get the accessors to append to model arrays.
 *
 * @return array
 */
public function getAppends()
{
    return $this->appends;
}

8. Str wrap() static method:

Steve Bauman contributed by adding the missing static method variant for the str($of)->wrap() method. It was previously available with the String helper.

Str::wrap('value')->wrap('"');
Str::of('value')->wrap('"');
str('value')->wrap('"');

// Outputs: "value"

Str::wrap('is', 'This ', ' me!');
Str::of('is')->wrap('This ', ' me!');
str('is')->wrap('This ', ' me!');

// Outputs: This is me!

Source File Edits: src/Illuminate/Support/Str.php

/**
 * Wrap the string with the given strings.
 *
 * @param  string  $before
 * @param  string|null  $after
 * @return string
 */
public static function wrap($value, $before, $after = null)
{
    return $before.$value.($after ??= $before);
}

9. Macroable Vite:

Tim MacDonald contributed by added the Macroable trait to the Illuminate\Support\Vite to create aliases to use in Blade by using the macro method matching your JS config.

Using JS configuration:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
 
export default defineConfig({
    plugins: [
        laravel(/* ... */),
    ],
    resolve: {
        alias: {
            'image': '/resources/images',
            'page': '/resources/js/Pages',
        },
    },
});
import Profile from '@image/profile.png'

Using Laravel configuration:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Vite;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}"));
    }
}
<img src="{{ Vite::image('profile.png') }}" ... >

Alises based on things like the apps environment:

Vite::macro('localeAsset', function ($asset) {
    $locale = config('app.locale');

    // resources/jp/images/*
    // resources/en/images/*
    // resources/fr/images/*
    return $this->asset("resources/{$locale}/images/{$asset}");
});
<img src="{{ Vite::localeAsset('logo.png') }}" alt="...">

Source File Edits: src/Illuminate/Foundation/Vite.php

<?php

...
use Illuminate\Support\Traits\Macroable;

class Vite implements Htmlable
{
    use Macroable;

    .....
}

Now the list of the Laravel 9.31 Released updates:

Added

  • Added unique deferrable initially deferred constants for PostgreSQL (#44127)
  • Request lifecycle duration handler (#44122)
  • Added Model::withoutTimestamps(…) (#44138)
  • Added manifestHash function to Illuminate\Foundation\Vite (#44136)
  • Added support for operator <=> in /Illuminate/Collections/Traits/EnumeratesValues::operatorForWhere() (#44154)
  • Added that Illuminate/Database/Connection::registerDoctrineType() can accept object as well as classname for new doctrine type (#44149)
  • Added Fake Batches (#44104#44173)
  • Added Model::getAppends() (#44180)
  • Added missing Str::wrap() static method (#44207)
  • Added require symfony/uid (#44202)
  • Make Vite macroable (#44198)

Fixed

  • Async fix in Illuminate/Http/Client/PendingRequest (#44179)
  • Fixes artisan serve command with PHP_CLI_SERVER_WORKERS environment variable (#44204)
  • Fixed InteractsWithDatabase::castAsJson($value) incorrectly handles SQLite Database (#44196)

Changed

  • Improve Blade compilation exception messages (#44134)
  • Improve test failure output (#43943)
  • Prompt to create MySQL db when migrating (#44153)
  • Improve UUID and ULID support for Eloquent (#44146)

To get to know more about Laravel, you can check these articles too.

You can check the latest release tech articles like Laravel 9.31 Released here.

Please follow and like us:

Related Posts

Leave a Reply

Share