DEV Community

Sumeet Shroff
Sumeet Shroff

Posted on • Originally published at mumbaiwebdesigner.com

The Laravel 13 Features That Matter in Real Projects

The Laravel 13 Features That Matter in Real Projects

Laravel 13 shipped on March 17, 2026, and the upgrade story is unusually simple: zero application-level breaking changes from Laravel 12, one hard requirement (PHP 8.3), and several features that are genuinely useful in production rather than just impressive in release notes.

This post focuses on the features you will actually reach for on real client projects — not an exhaustive tour. For the full release overview, upgrade checklist, and breaking changes reference, see Laravel 13: Features, Upgrade Guide, and Breaking Changes.

Prerequisites: PHP 8.3+, Laravel 13.x (latest stable: 13.14.0 as of June 2026), Composer 2.x.


1. PHP Attributes on Models and Controllers

Laravel 13 adds PHP 8-style #[Attribute] support across 15+ framework locations. The old property-based syntax still works — this is purely additive.

On Eloquent Models:

use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;

#[Table('posts', primaryKey: 'id', incrementing: true, timestamps: true)]
#[Fillable('title', 'body', 'user_id')]
#[Hidden('deleted_at')]
class Post extends Model {}
Enter fullscreen mode Exit fullscreen mode

On Controllers:

use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class CommentController extends Controller
{
    #[Middleware('subscribed')]
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post) { }
}
Enter fullscreen mode Exit fullscreen mode

When to actually use this: Attributes shine on large domain models where $fillable, $hidden, $casts, and relationship declarations are scattered across the class. Collocating table definition and mass assignment rules at the top of the file improves readability at a glance. On small CRUD models, the tradeoff is extra import lines for minimal gain.

Common mistake: Mass-converting every existing model to attribute syntax in a single PR. It creates a large, noisy diff with zero runtime benefit and can introduce subtle errors if you miss a property. Migrate incrementally, only when touching the file for another reason.


2. Cache::touch() — Extend TTL Without a Round-Trip

// Extends the expiry of 'session-token' by 2 hours without fetching or rewriting the value
Cache::touch('session-token', now()->addHours(2));
Enter fullscreen mode Exit fullscreen mode

Before this, extending a cache item's TTL required a get → put cycle, which wasted a round-trip and reset the value unnecessarily. Cache::touch() maps directly to:

  • Redis: a single EXPIRE command
  • Memcached: the native TOUCH operation

Practical use: Session-adjacent caches, expensive computed aggregates you want to keep alive while the page is being viewed, and rate-limit counters where you want sliding window behaviour without rewriting the counter.

Limitation you need to know: If you maintain a custom cache store driver, it must implement a touch(string $key, $seconds) method or you will get a fatal runtime error as soon as any code path calls Cache::touch(). Audit your custom drivers before upgrading.


3. Typed Configuration Retrieval

Laravel 12 had typed config helpers, but mismatches were caught at runtime — often in a code path that only ran under specific conditions. Laravel 13 throws ConfigTypeMismatchException at boot.

// Throws ConfigTypeMismatchException at boot if APP_DEBUG is not a boolean
$debug = config()->boolean('app.debug');

// Throws if CACHE_TTL is not an integer
$ttl = config()->integer('cache.ttl');
Enter fullscreen mode Exit fullscreen mode

Why this matters in real projects: Misconfigured environment variables are one of the most common causes of silent production bugs. A PHP string "true" passing as a boolean is a classic failure mode. With Laravel 13, your application will refuse to boot with an incorrect config type rather than misbehaving hours later in a specific feature.

Tradeoff: This is a fail-fast design. If your team is not used to boot-time exceptions, the first deployment after introducing typed config calls can be alarming. Add these calls deliberately, and test them in staging with the actual production .env shape before deploying.


4. JSON:API Resources Out of the Box

JSON:API compliance previously required a third-party package. Laravel 13 ships JsonApiResource natively.

use Illuminate\Http\Resources\JsonApi\JsonApiResource;

class PostResource extends JsonApiResource
{
    public function toAttributes($request): array
    {
        return [
            'title' => $this->title,
            'body'  => $this->body,
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

The response automatically uses application/vnd.api+json as the Content-Type and wraps the payload in the spec-compliant data.attributes structure.

Generate one with Artisan:

php artisan make:resource PostResource --json-api
Enter fullscreen mode Exit fullscreen mode

When to prefer this over existing packages: For teams building new APIs that need basic JSON:API compliance — resource objects, relationships, and sparse fieldsets — the built-in class handles the common cases with less setup. Teams with complex compound document requirements, or existing APIs built on cloudcreativity/laravel-json-api, are better served staying on their current package.


5. Queue::route() — Centralized Job Routing

Previously, assigning a job to a specific queue and connection required per-class configuration inside each job's $queue and $connection properties. Laravel 13 introduces centralized routing:

use Illuminate\Support\Facades\Queue;

// In a service provider or bootstrap/app.php
Queue::route(ProcessPodcast::class, queue: 'podcasts',   connection: 'redis');
Queue::route(SendEmailJob::class,   queue: 'emails',     connection: 'sqs');
Queue::route(GenerateReport::class, queue: 'reports',    connection: 'redis');
Enter fullscreen mode Exit fullscreen mode

This is particularly useful when job classes live in packages or vendor code and you cannot modify them directly. It also keeps infrastructure decisions — which connection, which queue — in a single place rather than embedded in business logic classes.

Discoverability tradeoff: Routing rules defined in a service provider are less immediately visible to developers reading a job class. Document the routing location in your project's README or leave a comment in the job class pointing to it.


6. Queue Inspection Methods (Added in 13.8.0)

Building admin panels or monitoring dashboards for queues previously required raw Redis or SQS calls. Laravel 13.8.0 adds first-party inspection:

use Illuminate\Support\Facades\Queue;

$pending  = Queue::allPendingJobs();   // all pending jobs across every queue
$reserved = Queue::allReservedJobs();  // jobs currently being processed
$delayed  = Queue::allDelayedJobs();   // jobs waiting for their delay to expire
Enter fullscreen mode Exit fullscreen mode

These return collections you can filter, count, and display. Worker pause and resume events are also added in the same release, enabling graceful drain-and-restart patterns during deployment.


7. The Laravel AI SDK Goes Stable

The AI SDK was in beta during the Laravel 12 era. It is now a fully stable, first-party API in Laravel 13. This is the one to re-evaluate if you dismissed it during the beta.

Basic text generation:

use Laravel\AI\Facades\AI;

$response = AI::text()
    ->using('openai', 'gpt-4o')
    ->prompt('Summarize this article: ' . $article)
    ->generate();

echo $response->text();
Enter fullscreen mode Exit fullscreen mode

Embeddings and vector search (with PostgreSQL + pgvector):

$embedding = AI::embeddings()
    ->using('openai', 'text-embedding-3-small')
    ->input('What is the refund policy?')
    ->generate();

$results = Post::query()
    ->nearestTo('embedding_column', $embedding->vector)
    ->limit(5)
    ->get();
Enter fullscreen mode Exit fullscreen mode

Supported providers: OpenAI, Anthropic (Claude), Google Gemini. The SDK also covers tool-calling agents, image generation, and audio synthesis and transcription through the same unified API.

Security note: AI provider credentials must live in .env only. The SDK reads from config/ai.php, which references environment variables. Never hard-code or commit API keys.

Tradeoff vs. community packages: Prism and the OpenAI PHP package may support newer model features sooner, since the first-party SDK moves at a measured pace. If you need access to cutting-edge model capabilities the day they release, evaluate community packages. For stable, Laravel-native usage, the first-party SDK is now the correct default.


The Only Real Blocker: PHP 8.3

With zero application-level breaking changes, the upgrade from Laravel 12 to 13 is straightforward — unless your hosting environment is still on PHP 8.2. PHP 8.2 reaches end of life in December 2026, so the Laravel 13 requirement is also a nudge to move off a runtime that will stop receiving security patches.

Before running composer update, confirm your PHP version:

php -v
# Should show PHP 8.3.x or higher
Enter fullscreen mode Exit fullscreen mode

If you are using Laravel Boost (laravel/boost ^2.0), the /upgrade-laravel-v13 command inside Claude Code, Cursor, or VS Code automates most of the mechanical upgrade steps. You still need to manually audit config/cache.php (the serializable_classes default changed to false) and your CSRF configuration.


Testing the Features You Adopt

  • PHP Attributes: Run php artisan model:show Post to confirm the model resolves its table and fillable rules correctly after converting to attribute syntax.
  • Cache::touch(): Write a unit test that asserts the TTL changes without the value changing. Use Cache::fake() and inspect the underlying store.
  • Typed config: Force a type mismatch in your local .env and confirm the ConfigTypeMismatchException is thrown at boot, not silently ignored.
  • Queue inspection: In a local Horizon or queue worker setup, dispatch a delayed job and call Queue::allDelayedJobs() from Tinker to verify it appears.
  • AI SDK: Test against the real provider API in a feature test with a real but low-cost call (e.g., a short embedding), and mock the facade in unit tests.

If you need Laravel development in Mumbai, Mumbai Web Designer builds production-grade Laravel applications.

Top comments (0)