Laravel
March 23, 2026

Laravel 13 Quiz: 12 Essential Questions With Detailed Answers For Developers

Test your Laravel 13 knowledge with these 12 curated questions. From PHP requirements to advanced attributes, get detailed explanations with working code examples.

S
Super Admin
35 views
0
0.4

Laravel 13 dropped on March 17, 2026, announced live by Taylor Otwell at Laracon EU 2026. This release carries a clear mission: AI-native workflows, stronger security defaults, and more expressive developer APIs - all while keeping breaking changes to an absolute minimum. Whether you are kicking off a new project or planning an upgrade from Laravel 12, the questions below will help you get up to speed quickly. Each answer includes practical code examples you can drop straight into your codebase.

Q1: What is the minimum PHP version required for Laravel 13?

Answer: Laravel 13 mandates PHP 8.3 or higher, dropping support for PHP 8.1 and 8.2 entirely. PHP 8.1 reached end-of-life in December 2025, so this shift aligns the framework with actively maintained runtimes. Laravel 13 also fully supports PHP 8.4 and 8.5, letting teams take advantage of JIT improvements and the native URI handling introduced in PHP 8.5 if desired.

# Verify your current PHP version before installing
php -v

# Minimum acceptable output for Laravel 13
# PHP 8.3.x (cli) or higher

# Upgrade on Ubuntu / Debian
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php8.3 php8.3-cli php8.3-mbstring php8.3-xml php8.3-bcmath php8.3-zip

The skeleton composer.json that ships with a fresh Laravel 13 project reflects this requirement explicitly:

{
    "require": {
        "php": "^8.3",
        "laravel/framework": "^13.0",
        "laravel/tinker": "^2.9"
    }
}

If your hosting environment still runs PHP 8.2, upgrading the runtime is the first task before attempting the framework upgrade. Tools like Rector can automate PHP version compatibility checks across your codebase.

Q2: How do you create a fresh Laravel 13 project using Composer?

Answer: The installation flow stays familiar, but you should pin the version constraint to ^13.0 so Composer resolves the correct major release. Alternatively, if you have the global Laravel installer (v5+) installed, laravel new now creates a Laravel 13 application by default as of March 2026.

# Option A - Composer create-project (recommended for CI / servers)
composer create-project laravel/laravel:^13.0 my-app

# Option B - Laravel installer (v5+ required)
laravel new my-app

# Navigate into the project and start the dev server
cd my-app
php artisan serve

# Confirm the version
php artisan --version
# Laravel Framework 13.x.x

After installation, Composer pulls in all dependencies, generates an application key, and scaffolds the .env file from the included example. You can start building immediately - no extra ceremony required.

Q3: How does Laravel 13 use PHP Attributes for Middleware and Authorization?

Answer: Laravel 13 expands first-party PHP attribute support across controllers, making it possible to declare middleware and policy checks directly on classes and methods using #[Middleware] and #[Authorize]. This approach keeps behavioral configuration co-located with the code it governs, improving readability and eliminating scattered route-file annotations. Queue-oriented jobs also gain new attributes like #[Tries], #[Backoff], #[Timeout], and #[FailOnTimeout].

<?php

namespace App\Http\Controllers;

use App\Models\Comment;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class CommentController
{
    // Extra middleware only on this method
    #[Middleware('subscribed')]
    // Policy check: current user must be able to 'create' a Comment on the given Post
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post)
    {
        // Validation and creation logic here
    }

    // No extra attribute needed - inherits the class-level #[Middleware('auth')]
    public function index(Post $post)
    {
        return $post->comments()->paginate(20);
    }
}

All attribute-based configuration is fully optional. Existing route files and $middleware arrays continue to work without modification, so teams can adopt attributes incrementally in new controllers without touching legacy code.

Q4: What is the Laravel AI SDK introduced in Laravel 13?

Answer: The first-party Laravel AI SDK is the headline feature of Laravel 13. It provides a unified, provider-agnostic API for text generation, tool-calling agents, embeddings, image synthesis, audio synthesis, and vector-store integrations. You configure your preferred provider (OpenAI, Anthropic, etc.) in config/ai.php and the SDK handles the rest, meaning swapping providers does not require rewriting application logic.

<?php

namespace App\Http\Controllers;

use Laravel\Ai\Facades\Ai;
use Laravel\Ai\Image;
use Laravel\Ai\Audio;

class ContentController extends Controller
{
    // Text generation - provider-agnostic
    public function summarise(string $text): string
    {
        return Ai::text("Summarise the following in three sentences:\n\n{$text}");
    }

    // Agent with tool-calling
    public function runSalesCoach()
    {
        $response = \App\Ai\Agents\SalesCoach::make()
            ->prompt('Analyse this sales transcript and suggest improvements.');

        return (string) $response;
    }

    // Image generation
    public function generateProductImage(string $description): string
    {
        $image = Image::of($description)->generate();
        return (string) $image; // raw binary content
    }

    // Text-to-speech
    public function narrate(string $script): string
    {
        $audio = Audio::synthesise($script);
        return (string) $audio;
    }
}

Existing third-party packages such as openai-php/laravel or echolabs/prism remain usable, but you should evaluate whether the first-party SDK covers your use cases before carrying those dependencies forward into a Laravel 13 project.

Q5: How does the new Cache::touch() method work in Laravel 13?

Answer: Cache::touch() extends the TTL of an existing cache entry without reading or rewriting the cached value itself. Previously, sliding-expiration logic required a get followed by a put, transferring the full payload unnecessarily. The new method maps to the most efficient native operation per driver: a single EXPIRE on Redis, a TOUCH command on Memcached, and a single UPDATE on the database driver. It returns true on success and false when the key does not exist.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class SlidingSessionCache
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        if ($user = $request->user()) {
            $key = "user_session:{$user->id}";

            // Extend the TTL by 30 minutes on every authenticated request
            // touch() is a no-op (returns false) if the key has already expired
            Cache::touch($key, now()->addMinutes(30));
        }

        return $response;
    }
}

// Standalone usage - refresh an expensive computation result
$cacheKey = "report:monthly:{$month}";

Cache::remember($cacheKey, 3600, fn () => $this->buildMonthlyReport($month));

// Later, if recent activity justifies keeping the result warm, extend it
if (Cache::touch($cacheKey, now()->addHour())) {
    logger("Cache TTL extended for {$cacheKey}");
}

Cache::touch() is implemented across all cache drivers: Array, APC, Database, DynamoDB, File, Memcached, Memoized, Null, and Redis - so behaviour is consistent regardless of your cache backend.

Q6: How do the new job attributes (#[Tries], #[Backoff], #[Timeout]) work?

Answer: Laravel 13 introduces PHP attributes to configure queued job behaviour declaratively at the class level. Instead of defining public properties like $tries or $timeout on the job class, you annotate the class with the corresponding attribute. This makes intent immediately visible without having to scan the class body for property declarations.

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\Attributes\Backoff;
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

#[Tries(5)]
#[Timeout(120)]           // Max 120 seconds per attempt
#[Backoff(30)]            // Wait 30 seconds between retries
#[FailOnTimeout]          // Mark job as failed (not released) when it times out
class SendWeeklyDigest implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public int $userId) {}

    public function handle(): void
    {
        $user = \App\Models\User::findOrFail($this->userId);
        $user->sendWeeklyDigestNotification();
    }

    public function failed(\Throwable $e): void
    {
        logger()->error("Weekly digest failed for user {$this->userId}: {$e->getMessage()}");
    }
}

The traditional public properties ($tries, $timeout, etc.) continue to work if you prefer them. Attributes simply offer a more co-located, self-documenting alternative that is especially useful when your team uses static analysis tools that can read PHP attribute metadata.

Q7: What vector / semantic search capabilities does Laravel 13 provide?

Answer: Laravel 13 deepens its search story with native vector query support built into the query builder. Using PostgreSQL with the pgvector extension, you can run semantic similarity searches directly in Eloquent or DB queries without installing third-party packages. The framework generates embeddings from plain strings using the AI SDK and compares them against stored vector columns.

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Models\Document;

class SearchController extends Controller
{
    // Semantic search using pgvector - no raw SQL required
    public function search(string $query)
    {
        $results = DB::table('documents')
            ->whereVectorSimilarTo('embedding', $query)
            ->limit(10)
            ->get();

        return response()->json($results);
    }

    // Eloquent-style semantic search
    public function searchDocuments(string $query)
    {
        return Document::whereVectorSimilarTo('embedding', $query)
            ->orderByVectorSimilarity('embedding', $query)
            ->limit(5)
            ->get();
    }
}

// Migration - add a vector column
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('documents', function (Blueprint $table) {
            $table->vector('embedding', dimensions: 1536)->nullable();
        });
    }
};

Laravel Scout is still the right choice for full-text search against Meilisearch or Typesense. The new vector APIs complement Scout by targeting use cases that require AI-generated embeddings and similarity ranking rather than keyword matching.

Q8: What are the breaking changes when upgrading from Laravel 12 to 13?

Answer: The official upgrade guide describes the jump from Laravel 12 to 13 as taking roughly ten minutes for typical applications. The breaking changes are narrow but require careful attention in four areas: request forgery protection, cache serialization, cache and session naming defaults, and custom contracts or cache store implementations.

// 1. PHP VERSION - mandatory first step
// Laravel 12: PHP 8.2+   |   Laravel 13: PHP 8.3+
// Update composer.json:
{
    "require": {
        "php": "^8.3",
        "laravel/framework": "^13.0",
        "phpunit/phpunit": "^12.0"
    }
}

// 2. CSRF / ORIGIN PROTECTION
// Laravel 13 adds an Origin-header check on state-changing requests.
// Standard Inertia.js and Livewire forms require no code changes.
// Custom CSRF flows or subdomain configs - review config/cors.php:
'allowed_origins' => ['https://app.yourdomain.com'],

// 3. CACHE SERIALIZATION
// Laravel 13 hardens object deserialization by default.
// If you store PHP objects in cache, declare them in config/cache.php:
'serializable_classes' => [
    \App\ValueObjects\Report::class,
    \App\ValueObjects\UserPreferences::class,
],

// 4. CACHE PREFIX & SESSION COOKIE DEFAULTS
// Framework-generated Redis prefixes and session cookie names changed.
// If you relied on the generated defaults, pin them explicitly in .env:
CACHE_PREFIX=my_app
SESSION_COOKIE=my_app_session

// 5. MySQL DELETE…JOIN
// Queries with ORDER BY and LIMIT now compile correctly.
// Previously they were silently ignored; now they execute - verify your
// delete-join batch operations behave as expected on staging first.

// 6. CUSTOM CONTRACTS
// Several contracts gained new methods. If your app implements framework
// interfaces directly, check for signature changes in the upgrade guide.

The Laravel Shift tool automates most mechanical changes and opens a pull request with reviewable commits, which saves significant time on larger codebases. Always run your full test suite on a staging environment before promoting to production.

Q9: What changed in Http::pool() concurrency behaviour?

Answer: In Laravel 13, Http::pool() defaults to a concurrency of 2 for true parallel execution. In previous versions, leaving the concurrency parameter unset resulted in requests executing serially - a silent footgun that caught many developers off guard when they expected concurrent HTTP calls but measured no throughput improvement.

<?php

use Illuminate\Support\Facades\Http;

// Laravel 13: these three requests now run with concurrency = 2 by default
[$usersResponse, $ordersResponse, $productsResponse] = Http::pool(fn ($pool) => [
    $pool->get('https://api.example.com/users'),
    $pool->get('https://api.example.com/orders'),
    $pool->get('https://api.example.com/products'),
]);

// Explicitly set concurrency if you need a different limit
[$a, $b, $c] = Http::pool(fn ($pool) => [
    $pool->get('https://api.example.com/users'),
    $pool->get('https://api.example.com/orders'),
    $pool->get('https://api.example.com/products'),
], concurrency: 3); // all three at once

echo $usersResponse->json('data');

If your application relies on serial execution within a pool (e.g., to respect a rate limit), pass concurrency: 1 explicitly so the intent is clear and behaviour does not change if the default ever shifts in a future release.

Q10: How does Reverb's database driver benefit queue monitoring?

Answer: Laravel Reverb ships a database-backed driver that persists real-time broadcast events to your existing database, removing the requirement for a separate Redis infrastructure when building live dashboards or queue monitoring UIs. Event payloads, channel subscriptions, and delivery confirmations are stored natively, giving smaller teams a simpler operational footprint. For high-traffic production workloads Redis remains the preferred driver, but the database driver is production-worthy for moderate loads.

# config/reverb.php - switch to the database driver
REVERB_DRIVER=database

# Publish and apply the Reverb migrations
php artisan vendor:publish --tag=reverb-migrations
php artisan migrate
<?php

// Broadcasting a queue job progress event from a trackable job
namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use App\Events\JobProgressUpdated;

class ProcessReportJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, InteractsWithSockets;

    public function __construct(public int $reportId) {}

    public function handle(): void
    {
        broadcast(new JobProgressUpdated($this->reportId, 25, 'Gathering data'));
        $this->gatherData();

        broadcast(new JobProgressUpdated($this->reportId, 70, 'Building report'));
        $this->buildReport();

        broadcast(new JobProgressUpdated($this->reportId, 100, 'Complete'));
    }
}
// Frontend - listening for progress via Laravel Echo + Reverb
import Echo from 'laravel-echo';

window.Echo = new Echo({ broadcaster: 'reverb', /* connection config */ });

Echo.private(`report.${reportId}`)
    .listen('JobProgressUpdated', (e) => {
        document.getElementById('progress').textContent = `${e.progress}% - ${e.status}`;
    });

Q11: What passkey authentication support does Laravel 13 add?

Answer: Laravel 13 introduces native passkey (WebAuthn / FIDO2) support as part of the starter kits, enabling password-free authentication out of the box. Passkeys use device-bound cryptographic credentials - fingerprint, face ID, or hardware key - eliminating stolen-password attacks and password reset overhead entirely. The implementation integrates with Fortify and the updated Breeze / Jetstream kits without requiring third-party packages.

<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Fortify\Http\Controllers\AuthenticatedSessionController;

class PasskeyController extends AuthenticatedSessionController
{
    // Register a new passkey for the authenticated user
    public function registerOptions(Request $request): array
    {
        return Auth::user()->generatePasskeyRegistrationOptions();
    }

    public function register(Request $request): \Illuminate\Http\JsonResponse
    {
        $request->user()->registerPasskey($request->validated());

        return response()->json(['status' => 'passkey_registered']);
    }

    // Authenticate using an existing passkey (no password required)
    public function authenticateOptions(): array
    {
        return Auth::generatePasskeyAuthenticationOptions();
    }

    public function authenticate(Request $request): \Illuminate\Http\JsonResponse
    {
        $user = Auth::authenticateWithPasskey($request->validated());
        Auth::login($user);

        return response()->json(['status' => 'authenticated']);
    }
}

The starter kits expose registration and authentication flows in the browser using the Web Authentication API. Existing applications can add passkeys alongside traditional passwords, letting users choose their preferred method during a migration window.

Q12: What is the recommended step-by-step process to upgrade from Laravel 12 to 13?

Answer: The Laravel team designed the upgrade path to be completable in about ten minutes for a well-maintained Laravel 12 application. The critical path is: upgrade PHP first, then the framework, then audit the four high-impact areas in the official upgrade guide.

# Step 1 - Upgrade PHP runtime to 8.3 or higher
php -v  # Confirm 8.3+

# Step 2 - Update composer.json dependencies
# Change laravel/framework to ^13.0 and phpunit/phpunit to ^12.0

# Step 3 - Run the upgrade
composer update

# Step 4 - Clear all application caches
php artisan optimize:clear

# Step 5 - Run your test suite on staging
php artisan test
// High-impact areas to review manually after composer update:

// A) config/cache.php - add serializable_classes if you store PHP objects
'serializable_classes' => [
    // List any PHP classes you persist in cache
],

// B) .env - pin cache prefix and session cookie name if you relied on defaults
CACHE_PREFIX=your_app_name
SESSION_COOKIE=your_app_name_session

// C) config/cors.php - review Origin settings if you have custom CSRF logic
// Standard Inertia.js / Livewire apps: no changes needed

// D) Check for custom framework interface implementations:
// Cache\Store, Queue\Job contracts gained new method signatures in v13

After confirming staging is green, deploy to production during a low-traffic window. Laravel Shift can automate most mechanical changes and open a reviewable pull request if you prefer a guided workflow. Bug fixes for Laravel 12 continue until August 2026, so there is no urgency - but early adoption unlocks the AI SDK, passkeys, and vector search features from day one.

Putting It All Together

Laravel 13, released March 17, 2026, is the most AI-forward release the framework has seen. The first-party AI SDK alone opens a category of features - semantic search, generative content, voice interfaces - that previously required stitching together multiple community packages. Combine that with passkey authentication, native vector queries, a cleaner PHP attribute system, and the efficient Cache::touch() method, and you have a release that genuinely moves the productivity needle.

The migration story from Laravel 12 is deliberately smooth. Zero framework-level breaking changes, a ten-minute upgrade path for most apps, and continued ecosystem compatibility with Livewire, Inertia.js, Filament, and the Spatie family mean there is very little reason to wait. The main prerequisite is PHP 8.3 - clear that hurdle and the rest falls into place quickly.

When rolling out Laravel 13 in your team, start with the AI SDK and the new job attributes in greenfield work. Reserve the PHP attribute migration for controllers and jobs when they are being touched anyway, rather than scheduling a big-bang refactor. The framework supports both styles side by side, so incremental adoption is the pragmatic path.

Have you started migrating your projects to Laravel 13? Which feature are you most excited about - the AI SDK, passkeys, or native vector search? Drop your thoughts in the comments below, and check back on StalkTechie for deep-dive tutorials on each of these features as the ecosystem matures.

0 Comments
Share:

Discussion

0 Comments

Join the conversation

Login to share your thoughts with the community

Related Tutorials