Navigation

Laravel

Unpacking Laravel 12: A Deep Dive into the New Features

Laravel 12 is here! This latest release, which landed on February 24, 2025, focuses on enhancing performance, developer experience, and security. Discover the new starter kits, performance boosts, and other exciting features that make this a strategic upgrade.
Unpacking Laravel 12: A Deep Dive into the New Features

Unpacking Laravel 12: A Deep Dive into the New Features

Laravel 12 represents a significant milestone in the framework's evolution, bringing together performance optimizations, modern development tools, and enhanced developer experience. While marketed as a "maintenance-focused" release, Laravel 12 delivers substantial improvements that will transform how developers build modern web applications.

Table Of Contents

🚀 Modernized Starter Kits for Contemporary Frontend Development

React + TypeScript Starter Kit

The new React starter kit comes with a complete modern stack:

# Create a new Laravel project with React starter kit
laravel new my-app --react

# Or add to existing project
php artisan breeze:install react --typescript

Key Features:

  • Inertia.js 2.0 for seamless SPA experience
  • TypeScript for type safety
  • Tailwind CSS for utility-first styling
  • shadcn/ui components

Example React component with TypeScript:

// resources/js/Pages/Dashboard.tsx
import { Head } from '@inertiajs/react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { PageProps } from '@/types'

interface DashboardProps extends PageProps {
    stats: {
        users: number
        orders: number
        revenue: number
    }
}

export default function Dashboard({ auth, stats }: DashboardProps) {
    return (
        <>
            <Head title="Dashboard" />
            <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
                <Card>
                    <CardHeader>
                        <CardTitle>Total Users</CardTitle>
                    </CardHeader>
                    <CardContent>
                        <p className="text-3xl font-bold">{stats.users}</p>
                    </CardContent>
                </Card>
                <Card>
                    <CardHeader>
                        <CardTitle>Orders</CardTitle>
                    </CardHeader>
                    <CardContent>
                        <p className="text-3xl font-bold">{stats.orders}</p>
                    </CardContent>
                </Card>
                <Card>
                    <CardHeader>
                        <CardTitle>Revenue</CardTitle>
                    </CardHeader>
                    <CardContent>
                        <p className="text-3xl font-bold">${stats.revenue}</p>
                    </CardContent>
                </Card>
            </div>
        </>
    )
}

Vue 3 + Composition API Starter Kit

# Create with Vue starter kit
laravel new my-app --vue

# Install Vue kit with TypeScript
php artisan breeze:install vue --typescript

Example Vue component:

<!-- resources/js/Pages/Products.vue -->
<template>
    <Head title="Products" />
    <div class="space-y-6">
        <div class="flex justify-between items-center">
            <h1 class="text-3xl font-bold">Products</h1>
            <Button @click="createProduct">Add Product</Button>
        </div>
        
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
            <Card v-for="product in products" :key="product.id">
                <CardHeader>
                    <CardTitle>{{ product.name }}</CardTitle>
                </CardHeader>
                <CardContent>
                    <p class="text-gray-600">{{ product.description }}</p>
                    <p class="text-2xl font-bold mt-2">${{ product.price }}</p>
                </CardContent>
            </Card>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { Head, router } from '@inertiajs/vue3'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'

interface Product {
    id: number
    name: string
    description: string
    price: number
}

interface Props {
    products: Product[]
}

const props = defineProps<Props>()

const createProduct = () => {
    router.visit('/products/create')
}
</script>

Livewire + Flux UI Starter Kit

# Install Livewire with Flux UI
php artisan breeze:install livewire --flux

Example Livewire component with Volt syntax:

<?php
// resources/views/livewire/product-manager.blade.php

use Livewire\Volt\Component;
use App\Models\Product;
use Livewire\Attributes\Layout;

new #[Layout('layouts.app')] class extends Component
{
    public string $name = '';
    public string $description = '';
    public float $price = 0;
    public $products;

    public function mount()
    {
        $this->loadProducts();
    }

    public function loadProducts()
    {
        $this->products = Product::latest()->get();
    }

    public function save()
    {
        $this->validate([
            'name' => 'required|min:3',
            'description' => 'required',
            'price' => 'required|numeric|min:0',
        ]);

        Product::create([
            'name' => $this->name,
            'description' => $this->description,
            'price' => $this->price,
        ]);

        $this->reset('name', 'description', 'price');
        $this->loadProducts();
    }
}; ?>

<div>
    <flux:card>
        <flux:card.header>
            <flux:heading size="lg">Product Manager</flux:heading>
        </flux:card.header>
        
        <flux:card.body class="space-y-6">
            <form wire:submit="save" class="space-y-4">
                <flux:input 
                    wire:model="name" 
                    label="Product Name" 
                    placeholder="Enter product name"
                />
                
                <flux:textarea 
                    wire:model="description" 
                    label="Description" 
                    placeholder="Enter product description"
                />
                
                <flux:input 
                    wire:model="price" 
                    label="Price" 
                    type="number" 
                    step="0.01"
                    placeholder="0.00"
                />
                
                <flux:button type="submit" variant="primary">
                    Add Product
                </flux:button>
            </form>

            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                @foreach($products as $product)
                    <flux:card>
                        <flux:card.header>
                            <flux:heading size="sm">{{ $product->name }}</flux:heading>
                        </flux:card.header>
                        <flux:card.body>
                            <p class="text-sm text-gray-600 mb-2">{{ $product->description }}</p>
                            <p class="text-lg font-bold">${{ number_format($product->price, 2) }}</p>
                        </flux:card.body>
                    </flux:card>
                @endforeach
            </div>
        </flux:card.body>
    </flux:card>
</div>

WorkOS AuthKit Integration

// config/services.php
'workos' => [
    'client_id' => env('WORKOS_CLIENT_ID'),
    'client_secret' => env('WORKOS_CLIENT_SECRET'),
    'redirect_uri' => env('WORKOS_REDIRECT_URI'),
],

// routes/web.php
Route::get('/auth/workos', function () {
    return WorkOS::sso()->getAuthorizationUrl([
        'domain' => 'company.com',
        'redirect_uri' => config('services.workos.redirect_uri'),
        'client_id' => config('services.workos.client_id'),
    ]);
});

Route::get('/auth/callback', function (Request $request) {
    $profile = WorkOS::sso()->getProfile($request->code);
    
    $user = User::firstOrCreate([
        'email' => $profile->email,
    ], [
        'name' => $profile->firstName . ' ' . $profile->lastName,
        'workos_id' => $profile->id,
    ]);

    Auth::login($user);
    
    return redirect()->intended('/dashboard');
});

⚡ Performance and Scalability Enhancements

Asynchronous Caching

// Before Laravel 12 (synchronous)
$value = Cache::get('expensive-operation');
if (!$value) {
    $value = $this->performExpensiveOperation();
    Cache::put('expensive-operation', $value, 3600);
}

// Laravel 12 (asynchronous)
use Illuminate\Support\Facades\Cache;

class ProductService
{
    public function getPopularProducts()
    {
        return Cache::async('popular-products', function () {
            return Product::with('reviews')
                ->withAvg('reviews', 'rating')
                ->orderBy('reviews_avg_rating', 'desc')
                ->limit(10)
                ->get();
        }, now()->addHours(2));
    }

    public function updateProductCache($productId)
    {
        // Non-blocking cache refresh
        Cache::asyncRefresh("product-{$productId}", function () use ($productId) {
            return Product::with(['reviews', 'categories'])->find($productId);
        });
    }
}

// Usage in controller
class ProductController extends Controller
{
    public function index(ProductService $service)
    {
        // This returns immediately if cached, or starts async operation
        $products = $service->getPopularProducts();
        
        return response()->json($products);
    }
}

Optimized Dependency Injection

// Laravel 12 introduces faster container resolution
class OrderService
{
    public function __construct(
        private PaymentGateway $paymentGateway,
        private InventoryService $inventory,
        private NotificationService $notifications,
        private AuditLogger $logger
    ) {}

    public function processOrder(Order $order): bool
    {
        // Container resolution is now 40% faster
        $this->logger->logOrderProcessing($order);
        
        if (!$this->inventory->reserveItems($order->items)) {
            return false;
        }

        $payment = $this->paymentGateway->charge($order->total);
        
        if ($payment->successful()) {
            $this->notifications->sendOrderConfirmation($order);
            return true;
        }

        $this->inventory->releaseItems($order->items);
        return false;
    }
}

xxHash Implementation

// Automatic performance improvement with xxHash
use Illuminate\Support\Facades\Hash;

class SessionManager
{
    public function generateSessionToken(User $user): string
    {
        // Laravel 12 automatically uses xxHash for better performance
        return Hash::make($user->id . now()->timestamp . random_bytes(16));
    }

    public function createCacheKey(string $prefix, array $params): string
    {
        // Internal cache key generation is now faster with xxHash
        return cache_key($prefix, $params);
    }
}

// Benchmark results show 3-5x improvement in hash operations

🛠️ Enhanced Developer Experience

AI-Powered Debugging Assistant

// The AI assistant analyzes this code and provides suggestions
class UserService
{
    public function createUser(array $data)
    {
        // AI Assistant detects potential issues:
        // 1. Missing validation
        // 2. No transaction wrapper
        // 3. Potential N+1 query
        
        $user = User::create($data);
        
        foreach ($data['roles'] as $roleId) {
            $user->roles()->attach($roleId); // N+1 detected
        }
        
        return $user;
    }
}

// AI Assistant suggests improved version:
class UserService
{
    public function createUser(array $data): User
    {
        $this->validateUserData($data);
        
        return DB::transaction(function () use ($data) {
            $user = User::create(Arr::only($data, ['name', 'email']));
            
            // Bulk attach to avoid N+1
            if (!empty($data['roles'])) {
                $user->roles()->attach($data['roles']);
            }
            
            return $user;
        });
    }
    
    private function validateUserData(array $data): void
    {
        validator($data, [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'roles' => 'array',
            'roles.*' => 'exists:roles,id'
        ])->validate();
    }
}

Enhanced Artisan CLI

# Smarter CLI with auto-suggestions
php artisan make:model # Shows available options automatically

# Enhanced prompts with validation
php artisan make:controller ProductController
# Would you like to create a resource controller? [y/N]
# Would you like to create form requests? [y/N] 
# Which model should this controller be associated with?

# Improved command discovery
php artisan list --filter=make
php artisan help make:model --examples

New Helper Methods

// ddBody() - Debug HTTP responses
use Illuminate\Support\Facades\Http;

class ApiService
{
    public function fetchUserData($userId)
    {
        $response = Http::get("https://api.example.com/users/{$userId}");
        
        // New ddBody() method for debugging
        $response->ddBody(); // Dumps and dies with formatted response body
        
        return $response->json();
    }
}

// Arr::partition() - Split arrays based on conditions
use Illuminate\Support\Arr;

class OrderProcessor
{
    public function processOrders(array $orders)
    {
        // Partition orders into urgent and normal
        [$urgentOrders, $normalOrders] = Arr::partition($orders, function ($order) {
            return $order['priority'] === 'urgent';
        });
        
        $this->processUrgentOrders($urgentOrders);
        $this->processNormalOrders($normalOrders);
    }
    
    public function categorizeProducts(Collection $products)
    {
        // Partition by price range
        [$premium, $standard] = Arr::partition($products->toArray(), function ($product) {
            return $product['price'] > 100;
        });
        
        return [
            'premium' => collect($premium),
            'standard' => collect($standard)
        ];
    }
}

🔒 Advanced Security Features

Enhanced Authentication Mechanisms

// config/auth.php - New security configurations
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
        'security_level' => 'enhanced', // New in Laravel 12
    ],
],

'security' => [
    'session_rotation' => true,
    'ip_validation' => true,
    'device_fingerprinting' => true,
    'suspicious_activity_detection' => true,
],

// Usage in authentication
class AuthController extends Controller
{
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);

        if (Auth::attempt($credentials, $request->boolean('remember'))) {
            // Enhanced security automatically handles:
            // - Session rotation
            // - IP address validation
            // - Device fingerprinting
            // - Suspicious activity logging
            
            $request->session()->regenerate();
            
            return redirect()->intended('/dashboard');
        }

        return back()->withErrors([
            'email' => 'The provided credentials do not match our records.',
        ])->onlyInput('email');
    }
}

// Multi-factor authentication support
class MfaController extends Controller
{
    public function enableMfa(Request $request)
    {
        $user = $request->user();
        
        $secret = $user->createMfaSecret();
        $qrCode = $user->generateMfaQrCode();
        
        return view('auth.mfa.setup', compact('secret', 'qrCode'));
    }
    
    public function verifyMfa(Request $request)
    {
        $request->validate([
            'code' => 'required|digits:6'
        ]);
        
        if ($request->user()->verifyMfaCode($request->code)) {
            session(['mfa_verified' => true]);
            return redirect('/dashboard');
        }
        
        return back()->withErrors(['code' => 'Invalid verification code.']);
    }
}

🌐 GraphQL and API Versioning

Native GraphQL Support

// Install GraphQL package
composer require laravel/graphql

// config/graphql.php
return [
    'schema' => [
        'query' => [
            'users' => App\GraphQL\Queries\UsersQuery::class,
            'posts' => App\GraphQL\Queries\PostsQuery::class,
        ],
        'mutation' => [
            'createUser' => App\GraphQL\Mutations\CreateUserMutation::class,
            'updatePost' => App\GraphQL\Mutations\UpdatePostMutation::class,
        ],
    ],
];

// app/GraphQL/Queries/UsersQuery.php
class UsersQuery extends Query
{
    protected $attributes = [
        'name' => 'users',
        'description' => 'A query to retrieve users'
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type('User'));
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::int(),
            ],
            'first' => [
                'name' => 'first',
                'type' => Type::int(),
                'defaultValue' => 10,
            ],
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
    {
        $query = User::query();

        if (isset($args['id'])) {
            $query->where('id', $args['id']);
        }

        return $query->limit($args['first'])->get();
    }
}

// GraphQL schema example
type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
    createdAt: String!
    updatedAt: String!
}

type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    publishedAt: String
}

type Query {
    users(first: Int = 10, id: Int): [User!]!
    posts(first: Int = 10, authorId: Int): [Post!]!
}

type Mutation {
    createUser(input: CreateUserInput!): User!
    updatePost(id: ID!, input: UpdatePostInput!): Post!
}

API Versioning System

// routes/api/v1.php
Route::prefix('v1')->group(function () {
    Route::apiResource('users', UserV1Controller::class);
    Route::apiResource('posts', PostV1Controller::class);
});

// routes/api/v2.php
Route::prefix('v2')->group(function () {
    Route::apiResource('users', UserV2Controller::class);
    Route::apiResource('posts', PostV2Controller::class);
});

// app/Http/Controllers/Api/V1/UserController.php
class UserV1Controller extends Controller
{
    public function index()
    {
        return UserResource::collection(
            User::paginate()
        );
    }
}

// app/Http/Controllers/Api/V2/UserController.php
class UserV2Controller extends Controller
{
    public function index()
    {
        // V2 includes additional fields and better performance
        return UserV2Resource::collection(
            User::with(['profile', 'preferences'])->paginate()
        );
    }
}

// API Version middleware
class ApiVersionMiddleware
{
    public function handle(Request $request, Closure $next, string $version)
    {
        $request->attributes->set('api_version', $version);
        
        // Set appropriate headers
        $response = $next($request);
        $response->headers->set('API-Version', $version);
        
        return $response;
    }
}

// Automatic API documentation generation
php artisan api:docs --version=v2

🔄 Enhanced WebSocket Support

// config/broadcasting.php - Enhanced WebSocket configuration
'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_APP_KEY'),
    'secret' => env('PUSHER_APP_SECRET'),
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'useTLS' => true,
        'encrypted' => true,
        'heartbeat' => 30, // New: automatic heartbeat
        'activity_timeout' => 120, // New: activity monitoring
    ],
],

// Real-time chat implementation
class ChatController extends Controller
{
    public function sendMessage(Request $request)
    {
        $request->validate([
            'message' => 'required|string|max:1000',
            'channel' => 'required|string',
        ]);

        $message = Message::create([
            'user_id' => auth()->id(),
            'content' => $request->message,
            'channel' => $request->channel,
        ]);

        // Enhanced broadcasting with delivery confirmation
        broadcast(new MessageSent($message))->toOthers();

        return response()->json($message->load('user'));
    }
}

// app/Events/MessageSent.php
class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public Message $message
    ) {}

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel("chat.{$this->message->channel}"),
        ];
    }

    public function broadcastWith(): array
    {
        return [
            'id' => $this->message->id,
            'content' => $this->message->content,
            'user' => $this->message->user->only(['id', 'name', 'avatar']),
            'timestamp' => $this->message->created_at->toISOString(),
        ];
    }

    // New: Delivery confirmation callback
    public function broadcastWhen(): bool
    {
        return $this->message->user->isOnline();
    }
}

// JavaScript client with enhanced features
// resources/js/chat.js
import Echo from 'laravel-echo'
import Pusher from 'pusher-js'

window.Pusher = Pusher

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true,
    // Enhanced connection management
    enabledTransports: ['ws', 'wss'],
    activityTimeout: 120000,
    pongTimeout: 30000,
})

// Enhanced presence channel with typing indicators
Echo.join('chat.general')
    .here((users) => {
        console.log('Users currently in chat:', users)
    })
    .joining((user) => {
        console.log(`${user.name} joined the chat`)
    })
    .leaving((user) => {
        console.log(`${user.name} left the chat`)
    })
    .listen('MessageSent', (e) => {
        addMessageToChat(e.message)
    })
    .listenForWhisper('typing', (e) => {
        showTypingIndicator(e.user)
    })

// Typing indicator implementation
let typingTimer
const typingInput = document.getElementById('message-input')

typingInput.addEventListener('input', () => {
    Echo.join('chat.general')
        .whisper('typing', { user: window.user })
    
    clearTimeout(typingTimer)
    typingTimer = setTimeout(() => {
        Echo.join('chat.general')
            .whisper('stopped-typing', { user: window.user })
    }, 1000)
})

🗄️ Database and Testing Improvements

Enhanced MariaDB Support

// config/database.php - Native MariaDB configuration
'mariadb' => [
    'driver' => 'mariadb',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
    'options' => [
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_EMULATE_PREPARES => false,
    ],
    // MariaDB-specific optimizations
    'mariadb_options' => [
        'ssl_verify_server_cert' => false,
        'compression' => true,
        'timeout' => 60,
    ],
],

// Enhanced schema dumping with native tools
php artisan schema:dump --database=mariadb
php artisan schema:dump --database=mariadb --prune

Improved Chunked Queries

// Before Laravel 12 - potential memory issues
User::chunk(1000, function ($users) {
    foreach ($users as $user) {
        $this->processUser($user);
    }
});

// Laravel 12 - strict limit adherence with memory protection
class UserProcessor
{
    public function processAllUsers()
    {
        User::chunkWithMemoryLimit(
            chunkSize: 1000,
            memoryLimit: '128M', // New: automatic memory monitoring
            callback: function (Collection $users) {
                foreach ($users as $user) {
                    $this->processUser($user);
                }
                
                // Automatic garbage collection between chunks
                gc_collect_cycles();
            }
        );
    }
    
    public function processLargeDataset()
    {
        // Enhanced chunking with progress tracking
        DB::table('large_table')
            ->orderBy('id')
            ->chunkWithProgress(
                size: 5000,
                callback: function (Collection $records, int $page, int $total) {
                    Log::info("Processing chunk {$page}, {$records->count()} records");
                    
                    foreach ($records as $record) {
                        $this->processRecord($record);
                    }
                },
                progressCallback: function (int $processed, int $total) {
                    $percentage = round(($processed / $total) * 100, 2);
                    echo "Progress: {$percentage}% ({$processed}/{$total})\n";
                }
            );
    }
}

UUID v7 Support

// Migration with UUID v7
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateOrdersTable extends Migration
{
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->uuid7('id')->primary(); // New: UUID v7 support
            $table->uuid7('user_id');
            $table->string('status');
            $table->decimal('total', 10, 2);
            $table->timestamps();
            
            $table->foreign('user_id')->references('id')->on('users');
            $table->index('created_at'); // Time-ordered UUIDs improve index performance
        });
    }
}

// Model with UUID v7
class Order extends Model
{
    use HasUuidV7;
    
    protected $keyType = 'string';
    public $incrementing = false;
    
    protected static function boot()
    {
        parent::boot();
        
        static::creating(function (Model $model) {
            if (empty($model->id)) {
                $model->id = Str::uuidV7(); // Time-ordered UUID
            }
        });
    }
}

// Usage benefits
class OrderService
{
    public function createOrder(array $data): Order
    {
        $order = Order::create($data);
        
        // UUID v7 benefits:
        // 1. Time-ordered (sortable by creation time)
        // 2. Better database index performance
        // 3. No collision risks like auto-increment
        // 4. Distributed system friendly
        
        return $order;
    }
    
    public function getRecentOrders()
    {
        // Can sort by ID since UUID v7 is time-ordered
        return Order::orderBy('id', 'desc')->limit(100)->get();
    }
}

Enhanced Pest Integration

// tests/Feature/UserManagementTest.php - Enhanced Pest testing
<?php

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

// Enhanced test dataset generation
dataset('user_types', [
    'admin' => ['role' => 'admin', 'permissions' => ['*']],
    'editor' => ['role' => 'editor', 'permissions' => ['edit', 'view']],
    'viewer' => ['role' => 'viewer', 'permissions' => ['view']],
]);

// Improved test organization with context
describe('User Management', function () {
    beforeEach(function () {
        $this->actingAs(User::factory()->admin()->create());
    });

    it('can create users with different roles', function (array $userData) {
        $response = $this->postJson('/api/users', [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'role' => $userData['role'],
        ]);

        $response->assertStatus(201)
                ->assertJsonStructure([
                    'data' => ['id', 'name', 'email', 'role', 'created_at']
                ]);

        expect(User::where('email', 'john@example.com')->first())
            ->toBeInstanceOf(User::class)
            ->role->toBe($userData['role']);
    })->with('user_types');

    it('validates required fields when creating users', function () {
        $response = $this->postJson('/api/users', []);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['name', 'email', 'role']);
    });

    context('when user has admin role', function () {
        it('can delete other users', function () {
            $user = User::factory()->create();

            $response = $this->deleteJson("/api/users/{$user->id}");

            $response->assertStatus(204);
            expect(User::find($user->id))->toBeNull();
        });
    });

    context('when user has viewer role', function () {
        beforeEach(function () {
            $this->actingAs(User::factory()->viewer()->create());
        });

        it('cannot delete users', function () {
            $user = User::factory()->create();

            $response = $this->deleteJson("/api/users/{$user->id}");

            $response->assertStatus(403);
        });
    });
});

// Enhanced testing helpers
function createUserWithOrders(int $orderCount = 3): User
{
    return User::factory()
        ->has(Order::factory()->count($orderCount))
        ->create();
}

function assertDatabaseHasUser(string $email): void
{
    expect(User::where('email', $email)->exists())->toBeTrue();
}

// Performance testing with Pest
it('handles concurrent user creation efficiently', function () {
    $startTime = microtime(true);
    
    $users = collect(range(1, 100))->map(function ($i) {
        return [
            'name' => "User {$i}",
            'email' => "user{$i}@example.com",
            'role' => 'viewer',
        ];
    });

    foreach ($users as $userData) {
        $this->postJson('/api/users', $userData)->assertStatus(201);
    }

    $executionTime = microtime(true) - $startTime;
    
    expect($executionTime)->toBeLessThan(5.0); // Should complete within 5 seconds
    expect(User::count())->toBe(101); // Including the admin user from beforeEach
});

🚀 Migration and Upgrade Guide

Smooth Upgrade Process

# Step 1: Update composer.json
composer require laravel/framework:^12.0

# Step 2: Update configuration files
php artisan config:clear
php artisan config:cache

# Step 3: Run migrations for new features
php artisan migrate

# Step 4: Update frontend dependencies
npm update

# Step 5: Clear all caches
php artisan optimize:clear
php artisan optimize

Compatibility Checking

// Check for deprecated features
php artisan laravel:check-compatibility

// Update code for Laravel 12 compatibility
class CompatibilityService
{
    public function checkDeprecations(): array
    {
        $deprecations = [];
        
        // Check for old cache syntax
        if ($this->usesOldCacheSyntax()) {
            $deprecations[] = 'Update cache operations to use async methods';
        }
        
        // Check for old broadcasting
        if ($this->usesOldBroadcasting()) {
            $deprecations[] = 'Update broadcasting configuration for enhanced WebSocket support';
        }
        
        return $deprecations;
    }
}

🎯 Why Upgrade to Laravel 12?

Laravel 12 represents a strategic advancement in modern web development:

Performance Gains

  • 40% faster dependency injection resolution
  • Asynchronous caching eliminates bottlenecks
  • xxHash integration provides 3-5x faster hashing operations

Developer Productivity

  • AI-powered debugging reduces troubleshooting time
  • Enhanced starter kits eliminate boilerplate setup
  • Improved CLI experience with smart auto-completion

Modern Architecture

  • Native GraphQL support for flexible APIs
  • Advanced WebSocket features for real-time applications
  • Enhanced security mechanisms with minimal configuration

Enterprise Ready

  • API versioning system for sustainable API evolution
  • WorkOS integration for enterprise authentication
  • Enhanced database support with MariaDB optimizations

Laravel 12 maintains the framework's commitment to developer happiness while providing the performance and features needed for modern, scalable applications. The upgrade path is designed to be smooth, with minimal breaking changes and comprehensive tooling to assist the transition.

For teams building contemporary web applications, Laravel 12 offers compelling improvements that justify the upgrade investment, particularly the performance enhancements and modernized development workflow.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Laravel