Navigation

Laravel

**Trick:** Throwing Exceptions Conditionally with `throw_if()` and `throw_unless()`

Trick: Throwing Exceptions Conditionally with throw_if() and throw_unless() Writing clean exception handling code doesn't have to be verbose. Instead of cluttering your code with if-statements followed by throw statements, you can use throw_if() and throw_unless() functions to make your code more re...

Writing clean exception handling code doesn't have to be verbose. Instead of cluttering your code with if-statements followed by throw statements, you can use throw_if() and throw_unless() functions to make your code more readable and concise.

Table Of Contents

The Problem with Traditional Exception Handling

Traditional exception throwing often looks like this:

if ($user->is_banned) {
    throw new ValidationException('User is banned');
}

if (!$user->isAuthenticated()) {
    throw new UnauthorizedException('Authentication required');
}

if (empty($data['email'])) {
    throw new InvalidArgumentException('Email is required');
}

This approach works but creates repetitive, verbose code that's harder to scan and maintain.

The Solution: Conditional Exception Functions

throw_if() - Throw When Condition is True

throw_if(condition, ExceptionClass::class, 'Error message');

Examples:

// Throw if user is banned
throw_if($user->is_banned, ValidationException::class, 'User is banned');

// Throw if file is too large
throw_if(filesize($file) > 5242880, FileSizeException::class, 'File exceeds 5MB limit');

// Throw if insufficient balance
throw_if($account->balance < $amount, InsufficientFundsException::class, 'Insufficient balance');

throw_unless() - Throw When Condition is False

throw_unless(condition, ExceptionClass::class, 'Error message');

Examples:

// Throw unless user is authenticated
throw_unless($user->isAuthenticated(), UnauthorizedException::class, 'Authentication required');

// Throw unless file exists
throw_unless(file_exists($filePath), FileNotFoundException::class, 'File not found');

// Throw unless user has permission
throw_unless($user->can('edit', $post), ForbiddenException::class, 'Permission denied');

Real-World Usage Patterns

Input Validation

function createUser($data) {
    throw_unless(isset($data['email']), ValidationException::class, 'Email required');
    throw_unless(isset($data['password']), ValidationException::class, 'Password required');
    throw_if(strlen($data['password']) < 8, ValidationException::class, 'Password too short');
    
    return User::create($data);
}

API Endpoint Guards

function updatePost($id, $data) {
    $post = Post::find($id);
    
    throw_unless($post, ModelNotFoundException::class, 'Post not found');
    throw_unless(auth()->user()->can('update', $post), ForbiddenException::class, 'Unauthorized');
    throw_if($post->is_locked, ValidationException::class, 'Post is locked');
    
    return $post->update($data);
}

Business Logic Validation

function processPayment($order, $paymentMethod) {
    throw_unless($order->isPending(), InvalidStateException::class, 'Order already processed');
    throw_if($paymentMethod->isExpired(), PaymentException::class, 'Payment method expired');
    throw_unless($order->total > 0, ValidationException::class, 'Invalid order total');
    
    // Process payment logic
}

Implementation for Frameworks Without Built-in Support

If your framework doesn't provide these functions, create them:

function throw_if($condition, $exception, $message = null) {
    if ($condition) {
        throw new $exception($message ?? 'Condition was true');
    }
}

function throw_unless($condition, $exception, $message = null) {
    if (!$condition) {
        throw new $exception($message ?? 'Condition was false');
    }
}

Quick Tips

  1. Use descriptive messages: Make error messages actionable
  2. Choose appropriate exceptions: Use specific exception types for different error categories
  3. Keep conditions simple: Avoid complex logic in the condition parameter
  4. Chain validations: Group related conditional throws together

Before vs After

Before:

if (!$user->isActive()) {
    throw new UserException('User account is inactive');
}

if ($user->loginAttempts >= 3) {
    throw new SecurityException('Too many login attempts');
}

if (!$user->hasVerifiedEmail()) {
    throw new ValidationException('Email not verified');
}

After:

throw_unless($user->isActive(), UserException::class, 'User account is inactive');
throw_if($user->loginAttempts >= 3, SecurityException::class, 'Too many login attempts');
throw_unless($user->hasVerifiedEmail(), ValidationException::class, 'Email not verified');

The result is cleaner, more readable code that clearly communicates intent while reducing boilerplate. Start using throw_if() and throw_unless() in your next project to write more expressive exception handling code!

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Laravel