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
5 views
0
2

Laravel 13 Quiz: 12 Essential Questions With Detailed Answers

Laravel 13 brings significant changes to the framework with PHP 8.3 as the minimum requirement, native attribute support for routing and Eloquent, and several breaking changes from previous versions. Whether you are upgrading an existing application or starting a new project, understanding these features is crucial. This quiz covers the most important aspects of Laravel 13 with practical code examples you can use immediately.

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

Answer: Laravel 13 requires PHP 8.3 or higher. This represents a significant jump from Laravel 10 which supported PHP 8.1, and Laravel 11 which required PHP 8.2. The framework leverages PHP 8.3 features including typed class constants, deep cloning of readonly properties, and improvements to the randomizer extension. When setting up a new server or local environment for Laravel 13, ensure your PHP version meets this requirement or you will encounter composer dependency errors during installation.

# Check your current PHP version
php -v

# Output should show PHP 8.3.x or higher
# PHP 8.3.0 (cli) (built: Nov 21 2024 12:00:00)

# If you need to upgrade on Ubuntu/Debian
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php8.3 php8.3-cli php8.3-common php8.3-mbstring php8.3-xml php8.3-bcmath php8.3-zip

The composer.json file of Laravel 13 explicitly defines this requirement:

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

Q2: How to create Laravel 13 project using composer create-project command?

Answer: Creating a new Laravel 13 project follows the same pattern as previous versions, but you need to specify the version constraint to ensure you get Laravel 13. The command downloads the latest Laravel installer, creates a new project with all dependencies, and sets up the basic directory structure. After installation, you can immediately start the development server and access your application.

# Create a new Laravel 13 project explicitly
composer create-project laravel/laravel:^13.0 my-laravel13-app

# Alternative method using the Laravel installer (if updated)
laravel new my-laravel13-app --version=13.x

# Navigate to project directory
cd my-laravel13-app

# Start the development server
php artisan serve

# Expected output: Laravel development server started: http://127.0.0.1:8000

The composer create-project command performs several steps: it downloads the Laravel skeleton, installs all composer dependencies, generates the application key, and creates the .env file from the example template. You can verify the installation by checking the framework version in composer.json or running php artisan --version.

Q3: What is the new routing attributes syntax in Laravel 13?

Answer: Laravel 13 introduces native PHP attributes for routing, moving away from traditional route service providers. This feature lets you define routes directly in your controller classes using attributes like #[Get], #[Post], #[Put], and #[Delete]. The syntax is cleaner and keeps route definitions closer to the code they execute. This approach is especially useful for API-centric applications where each controller method maps directly to an endpoint.

validated());
        return response()->json(\$user, 201);
    }

    #[Put('/users/{id}', name: 'users.update')]
    public function update(UpdateUserRequest \$request, int \$id)
    {
        \$user = User::findOrFail(\$id);
        \$user->update(\$request->validated());
        return \$user;
    }

    #[Delete('/users/{id}', name: 'users.destroy')]
    public function destroy(int \$id)
    {
        User::findOrFail(\$id)->delete();
        return response()->noContent();
    }

    // Group middleware using attributes
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['store', 'update', 'destroy']),
        ];
    }
}

The framework automatically scans controllers in configured namespaces and registers routes based on these attributes. You can still use traditional route files if you prefer, but attributes provide a more maintainable approach for controller-heavy applications.

Q4: How to use typed properties in Blade views?

Answer: Laravel 13 extends PHP 8.3 typed properties support directly into Blade templates. When passing data to views, you can now type-hint the variables your view expects, making the code more predictable and enabling better IDE support. This feature works by declaring the expected types at the top of your Blade file using a @props directive with type declarations, similar to how you would type-hint constructor properties in a PHP class.

{{-- resources/views/components/user-profile.blade.php --}}
@props([
    'user' => \App\Models\User::class,
    'showEmail' => 'bool',
    'displayName' => '?string', // nullable string
    'roles' => 'array',
    'metadata' => 'object',
])

When using the component, Laravel validates the types automatically:

{{-- Calling the component with proper types --}}
@php
    \$metadata = new stdClass();
    \$metadata->lastLogin = now();
@endphp



{{-- This would throw a TypeError if you pass wrong types --}}
{{--  --}}

This feature catches type mismatches early, preventing unexpected null values or type juggling issues in your templates. The type syntax follows PHP 8.3 standards including union types, nullable types, and class names.

Q5: What is the benefit of #[Column] attribute in Eloquent models?

Answer: The #[Column] attribute in Laravel 13 replaces traditional protected properties like $table, $primaryKey, and $casts with modern PHP 8 attributes directly on your model properties. This approach keeps column configuration closer to the property definitions and makes models more readable. You can define column types, casting behavior, and database-specific options without separate protected arrays. The attribute also enables better IDE autocompletion and static analysis.

hasMany(Post::class);
    }
}

The attribute accepts parameters like name (custom column name), primary (mark as primary key), cast (type casting), unique (add unique validation), and default (default value). This approach eliminates the need for separate $casts, $dates, or $attributes properties, making the model definition more cohesive and maintainable.

Q6: How to extend Cache::touch() method with TTL in Laravel 13?

Answer: Laravel 13 introduces a new touch method on the Cache facade that updates the expiration time of existing cache items. This is useful for sliding expiration scenarios where you want to extend the lifetime of frequently accessed items. The touch method checks if an item exists and updates its TTL without retrieving or modifying the actual cached value, saving resources compared to a get followed by put operation.

user()) {
            \$cacheKey = 'user:' . \$request->user()->id . ':session';
            
            // Check if exists and extend TTL by 30 minutes
            if (Cache::has(\$cacheKey)) {
                Cache::touch(\$cacheKey, now()->addMinutes(30));
            }
        }

        return \$response;
    }
}

// In a controller or job
use Illuminate\Support\Facades\Cache;

class ProcessUserData
{
    public function handle(int \$userId)
    {
        \$cacheKey = "user_data:{\$userId}";
        
        // Store expensive computation result
        \$data = Cache::remember(\$cacheKey, 3600, function () use (\$userId) {
            return \$this->expensiveOperation(\$userId);
        });

        // Later, if user accesses related data, extend the cache
        if (\$this->shouldExtendCache(\$userId)) {
            // Extends by another hour from now
            Cache::touch(\$cacheKey, 3600);
        }

        return \$data;
    }

    // Example with sliding expiration in a scheduled job
    public function refreshActiveUsers()
    {
        \$activeUsers = Cache::get('active_user_ids', []);
        
        foreach (\$activeUsers as \$userId) {
            \$key = "user_profile:{\$userId}";
            
            // Extend TTL for active users
            if (Cache::has(\$key)) {
                Cache::touch(\$key, now()->addDay());
            }
        }
    }
}

The touch method accepts either a relative TTL in seconds or an absolute DateTime instance. If the key does not exist, touch returns false without throwing an exception. This makes it safe to use in production code without additional existence checks, though checking with has first can prevent unnecessary operations.

Q7: What changes came in Breeze or Jetstream for authentication setup?

Answer: Laravel 13 introduces significant updates to both Breeze and Jetstream authentication starter kits. The most notable change is the adoption of Livewire v4 for Livewire stacks and Inertia v2 for React and Vue stacks. Both kits now use the new routing attributes syntax internally, and include native support for Laravel 13's typed properties in Blade components. The installation process has been streamlined with fewer prompts and better defaults.

# Installing Laravel Breeze with different stacks
composer require laravel/breeze:^2.0

# Blade with Alpine (default, now uses Livewire v4)
php artisan breeze:install blade

# Livewire (Volt) with new attributes support
php artisan breeze:install livewire --volt

# React with Inertia v2
php artisan breeze:install react --inertia

# Vue with Inertia v2
php artisan breeze:install vue --inertia

# API only with Sanctum
php artisan breeze:install api

# Installing Laravel Jetstream
composer require laravel/jetstream:^5.0

# Jetstream with Livewire v4
php artisan jetstream:install livewire

# Jetstream with Inertia v2 and Teams
php artisan jetstream:install inertia --teams

The generated authentication views now use typed properties and the new #[Column] attributes in user models. Password validation rules are stricter by default, requiring mixed case and numbers. The profile update forms include two-factor authentication setup directly in the main interface rather than a separate screen. Additionally, both kits now support dark mode out of the box with proper CSS variables and theme switching.

{{-- Example of updated profile form with typed props --}}
@props([
    'user' => \App\Models\User::class,
    'sessions' => 'array',
])

@csrf @method('patch')
Save @if (session('status') === 'profile-updated')

Saved.

@endif

Q8: Example of new request classes in form validation?

Answer: Laravel 13 enhances form request classes with PHP 8 attributes for validation rules. Instead of defining rules in a method, you can now use the #[Validate] attribute directly on properties, making the request class more declarative. This approach works seamlessly with typed properties and provides better IDE support. You can combine attribute-based validation with custom rules, and the authorization logic remains in the authorize method.

user()->can('create', User::class);
    }

    // You can still use the prepareForValidation method
    protected function prepareForValidation(): void
    {
        \$this->merge([
            'email' => strtolower(\$this->email),
        ]);
    }

    // Custom messages using attributes or method
    protected function messages(): array
    {
        return [
            'email.unique' => 'This email is already registered.',
            'roles.*.exists' => 'One or more selected roles are invalid.',
        ];
    }
}

// Usage in controller
use App\Http\Requests\StoreUserRequest;

class UserController extends Controller
{
    #[Post('/users')]
    public function store(StoreUserRequest \$request)
    {
        // Validation already applied, access validated data directly
        \$user = User::create(\$request->validated());
        
        if (\$request->sendWelcomeEmail) {
            \$user->sendWelcomeNotification();
        }

        return response()->json(\$user, 201);
    }
}

The validated method returns an array of the validated data, but you can also access the properties directly since they are typed and populated after validation. For nested validation, you can use multiple Validate attributes as shown with the roles array.

Q9: How to apply #[Guarded] and #[Touches] attributes on models?

Answer: Laravel 13 introduces #[Guarded] and #[Touches] attributes as modern replacements for the traditional $guarded property and touches array. The #[Guarded] attribute marks model properties that should not be mass-assignable, while #[Touches] automatically updates parent model timestamps when relationships change. These attributes work alongside the existing #[Column] attribute to provide a complete attribute-based model configuration system.

belongsTo(Post::class);
    }

    public function user(): BelongsTo
    {
        return \$this->belongsTo(User::class);
    }
}

// Parent Post model
use Illuminate\Database\Eloquent\Attributes\Column;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    #[Column]
    protected int \$id;

    #[Column]
    protected string \$title;

    #[Column]
    protected string \$content;

    #[Column(cast: 'datetime')]
    protected Carbon \$updatedAt;

    public function comments(): HasMany
    {
        return \$this->hasMany(Comment::class);
    }
}

// Usage example
\$comment = new Comment(['content' => 'Great post!', 'user_id' => 1]);
// 'content' and 'user_id' are guarded, so this would not set them
// Instead, you need to set them individually
\$comment->content = 'Great post!';
\$comment->user_id = 1;
\$comment->post_id = \$post->id;
\$comment->save();

// When you save this comment, Post::updated_at automatically updates
// because of the #[Touches(['post'])] attribute

The #[Touches] attribute accepts an array of relationship names that should be touched when the model is saved or deleted. This replaces the traditional protected $touches = ['post'] property. The #[Guarded] attribute can be applied to any property that should be protected from mass assignment, eliminating the need for a separate $guarded array at the class level.

Q10: What support does Reverb DB driver provide for queues?

Answer: Laravel 13's Reverb package adds a database driver for real-time queue monitoring and management. This driver stores queue job status, progress, and metrics directly in your database, enabling you to build custom dashboards and monitoring tools. It provides real-time updates on queue health, job execution times, failure rates, and allows manual job intervention through a simple API. The DB driver works alongside existing queue drivers like Redis or Database, adding a monitoring layer without changing your job processing logic.

 env('REVERB_DRIVER', 'database'),
    
    'database' => [
        'connection' => env('DB_CONNECTION', 'mysql'),
        'table' => 'queue_monitor',
        'prune_after' => 604800, // 7 days in seconds
    ],
    
    'broadcasting' => [
        'enabled' => true,
        'channel' => 'queue-monitor',
    ],
];

// Publish and run migrations
php artisan vendor:publish --tag=reverb-migrations
php artisan migrate

// Job with progress tracking
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Laravel\Reverb\Concerns\Trackable;

class ProcessVideoJob implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels, Trackable;

    public function handle(): void
    {
        // Update progress at different stages
        \$this->updateProgress(10, 'Downloading video');
        
        // Simulate work
        sleep(2);
        
        \$this->updateProgress(40, 'Transcoding');
        sleep(3);
        
        \$this->updateProgress(80, 'Optimizing');
        sleep(2);
        
        \$this->updateProgress(100, 'Complete');
    }

    public function failed(\Throwable \$exception): void
    {
        \$this->markAsFailed(\$exception->getMessage());
    }
}

// Queue monitoring dashboard controller
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Laravel\Reverb\Facades\Reverb;

class QueueMonitorController extends Controller
{
    public function index()
    {
        \$metrics = [
            'active' => Reverb::getActiveJobs(),
            'failed' => Reverb::getFailedJobs(limit: 50),
            'completed' => Reverb::getCompletedJobs(last: 3600),
            'average_wait' => Reverb::getAverageWaitTime('default'),
            'throughput' => Reverb::getJobsPerMinute('default'),
            'slowest_queries' => Reverb::getSlowestJobs(10),
        ];

        return view('monitor.queues', \$metrics);
    }

    public function retryFailed(int \$jobId)
    {
        Reverb::retryJob(\$jobId);
        return back()->with('status', 'Job requeued');
    }

    public function cancelJob(int \$jobId)
    {
        Reverb::cancelJob(\$jobId);
        return back()->with('status', 'Job cancelled');
    }
}

// Real-time updates via broadcasting

The DB driver automatically tracks queue job lifecycle events: pushed, reserved, released, completed, or failed. You can query this data to build alerting systems, identify bottlenecks, and optimize queue worker configurations. The broadcasting integration provides real-time updates to connected clients, enabling live dashboards without polling.

Q11: What are the breaking changes when upgrading from Laravel 10 to 13?

Answer: Upgrading from Laravel 10 to 13 involves several breaking changes that require code modifications. The most significant changes include the minimum PHP version bump to 8.3, removal of deprecated methods, changes to the default session cookie name, and updates to the authentication scaffolding. The upgrade path typically goes through Laravel 11 first, then to 12, and finally to 13, with each version introducing its own set of changes.

 env(
    'SESSION_COOKIE',
    Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
// Previously used hyphens, now uses underscores

// 3. Removed Deprecated Methods
// Old Laravel 10 code
\$collection->contains(\$value); // Using loose comparison
// Laravel 13 requires strict typing
\$collection->containsStrict(\$value); // For strict comparison
\$collection->contains(\$value, strict: true); // Alternative

// 4. Database Query Builder Changes
// Old way with ambiguous column selection
DB::table('users')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->select('name', 'title')
    ->get();
// New way requires explicit table references
DB::table('users')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->select('users.name', 'posts.title') // Table qualified
    ->get();

// 5. Authentication Scaffolding
// Laravel 10 used Laravel Breeze 1.x
composer require laravel/breeze:^1.0
// Laravel 13 uses Breeze 2.x with new attributes
composer require laravel/breeze:^2.0
php artisan breeze:install

// 6. Pagination Defaults
// config/app.php
'pagination' => [
    'per_page' => 15, // Now required, previously optional
],

// 7. Mail Facade to Mail Manager
// Laravel 10
use Illuminate\Support\Facades\Mail;
Mail::to(\$user)->send(new WelcomeMail());
// Laravel 13 still supports this, but internal changed
// If you extended Mail::, update to MailManager

// 8. Validation Rule Changes
// Laravel 10 custom rule object
class CustomRule implements Rule
{
    public function passes(\$attribute, \$value) {}
    public function message() {}
}
// Laravel 13 uses Closure or invokable classes
use Illuminate\Contracts\Validation\ValidationRule;

class CustomRule implements ValidationRule
{
    public function validate(string \$attribute, mixed \$value, Closure \$fail): void
    {
        if (\$value === 'invalid') {
            \$fail("The {\$attribute} is invalid.");
        }
    }
}

// 9. Factory Namespace Change
// Laravel 10
use Database\Factories\UserFactory;
// Laravel 13 requires full namespace
use Database\Factories\UserFactory;
// Or use HasFactory trait which now uses classname resolution

// 10. Environment File Handling
// Laravel 13 loads .env more strictly
// Invalid .env syntax now throws exceptions instead of warnings
// Ensure your .env file follows the correct format:
APP_NAME="My App" # Quotes for spaces
DB_PASSWORD=secret#no#comments # No inline comments

Before upgrading, review the official upgrade guides for each intermediate version. Laravel provides upgrade helpers and automated tools to identify breaking changes in your codebase. The most common issues stem from the PHP 8.3 requirement and the removal of long-deprecated methods. Test thoroughly in a staging environment before deploying to production.

Q12: How to use PHP attributes in jobs and commands?

Answer: Laravel 13 extends PHP 8 attributes support to jobs and commands, allowing you to configure behavior declaratively. For jobs, attributes like #[ShouldQueue] and #[WithoutOverlapping] replace interface implementations and trait usage. For commands, attributes such as #[AsCommand] define the command signature and description directly in the class. This approach reduces boilerplate code and keeps configuration with the class they configure.

userId);
        \$user->calculateComplexMetrics();
        \$user->updateCaches();
    }

    #[RetryUntil(now()->addHours(2))]
    public function retryUntil(): \DateTime
    {
        return now()->addHours(2);
    }
}

// Console commands with attributes
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Console\Attributes\AsCommand;
use Illuminate\Console\Attributes\WithInput;
use Illuminate\Console\Attributes\WithOutput;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand(
    name: 'user:sync',
    description: 'Sync users with external CRM',
    hidden: false
)]
class SyncUsersCommand extends Command
{
    #[WithInput(option: 'limit', mode: InputOption::VALUE_OPTIONAL)]
    protected int \$limit = 100;

    #[WithInput(argument: 'source', description: 'CRM source (salesforce|hubspot)')]
    protected string \$source;

    public function handle()
    {
        \$this->info("Starting sync from {\$this->source}...");
        
        \$users = ExternalCRM::getUsers(\$this->source, \$this->limit);
        
        \$bar = \$this->output->createProgressBar(count(\$users));
        \$bar->start();

        foreach (\$users as \$userData) {
            \$this->syncUser(\$userData);
            \$bar->advance();
        }

        \$bar->finish();
        \$this->newLine();
        \$this->info('Sync completed successfully!');
    }

    #[WithOutput(name: 'sync-summary')]
    private function syncUser(array \$userData): void
    {
        // Sync logic
    }
}

// Middleware attributes for jobs
use Illuminate\Queue\Attributes\Middleware;
use App\Jobs\Middleware\RateLimited;

class ProcessPaymentJob
{
    #[Middleware([
        RateLimited::class,
        'throttle:payments',
    ])]
    public function middleware(): array
    {
        return []; // Empty because attributes handle it
    }

    public function handle(): void
    {
        // Process payment
    }
}

// Using attributes in scheduled commands
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Console\Attributes\Schedule as ScheduleAttr;

class GenerateReportsCommand extends Command
{
    #[ScheduleAttr(cron: '0 2 * * *', withoutOverlapping: true)]
    public function schedule(Schedule \$schedule): void
    {
        // This method is automatically called due to the attribute
        \$schedule->command(static::class)
            ->dailyAt('02:00')
            ->withoutOverlapping()
            ->appendOutputTo(storage_path('logs/reports.log'));
    }

    public function handle()
    {
        // Generate reports
    }
}

These attributes work by being read during framework bootstrapping. For jobs, Laravel scans queued job classes for attributes and applies the corresponding middleware or configurations. For commands, the artisan command registry uses attributes to build the command list without needing to manually add commands to Kernel. This attribute-based approach provides better IDE support and makes the code more self-documenting.

Putting It All Together

Laravel 13 represents a significant evolution in the framework's approach to PHP features. By embracing PHP 8.3 attributes throughout routing, Eloquent, jobs, and commands, it offers a more declarative and type-safe development experience. The minimum PHP version bump ensures the framework can leverage modern language features without backward compatibility constraints.

For teams upgrading from Laravel 10, the path requires careful planning due to breaking changes across multiple versions. However, the benefits include better performance through Reverb's queue monitoring, cleaner code with attribute-based configuration, and improved developer experience with typed properties in Blade views. The authentication kits have matured with better defaults and modern JavaScript framework support.

When adopting Laravel 13, start with the PHP 8.3 upgrade, then gradually introduce attributes in new code rather than refactoring everything at once. The framework maintains backward compatibility for traditional approaches, so you can mix old and new styles during the transition. Focus on understanding the #[Column] attribute for models and routing attributes for controllers first, as these provide the most immediate benefits for most applications.

Have you started using Laravel 13 in your projects? Which of these features are you most excited about? Share your experiences in the comments below, and check back for more in-depth tutorials on specific Laravel 13 features.

0 Comments
Share:

Discussion

0 Comments

Join the conversation

Login to share your thoughts with the community

Related Tutorials