Skip to content

Conditional Logic

Building responses that adapt to different request types, user permissions, and application state is essential in Hyper applications. This section covers the patterns and methods for creating conditional responses that serve both Hyper and traditional HTTP requests.

Detecting Hyper Requests

request()->isHyper()

Check if the current request is a Hyper request:

php
public function index()
{
    $products = Product::paginate(10);

    if (request()->isHyper()) {
        return hyper()->fragment('products.index', 'product-list', compact('products'));
    }

    return view('products.index', compact('products'));
}

The isHyper() method checks for the Datastar-Request header that Datastar automatically sends with every request. This allows you to provide different responses for Hyper interactions versus initial page loads.

@ifhyper Directive

In Blade templates, use @ifhyper to conditionally render content:

blade
@ifhyper
    <!-- Content rendered only for Hyper requests -->
    <div id="product-list">
        @foreach($products as $product)
            <x-product-card :product="$product" />
        @endforeach
    </div>
@else
    <!-- Content rendered for initial page loads -->
    <div id="product-list">
        @foreach($products as $product)
            <x-product-card :product="$product" />
        @endforeach
    </div>

    <div class="pagination">
        {{ $products->links() }}
    </div>
@endifhyper

This is useful when your fragment and full page views need slight differences in layout or structure.

Dual Responses

The web() Method

The web() method provides an elegant way to handle both Hyper and traditional requests in a single controller method:

php
public function index()
{
    $contacts = Contact::latest()->paginate(10);

    return hyper()
        ->fragment('contacts.index', 'contact-list', compact('contacts'))
        ->web(view('contacts.index', compact('contacts')));
}

When a Hyper request arrives, the fragment is returned. When a traditional browser request arrives (initial page load, direct URL access), the full view is returned instead.

How It Works

The web() method sets a fallback response that's only used for non-Hyper requests:

php
// Hyper request → Returns fragment
// Traditional request → Returns full view

return hyper()
    ->fragment('todos.index', 'todo-list', $data)
    ->web(view('todos.index', $data));

See the Dual Responses section for detailed information on this pattern.

Conditional Signal Updates

Update signals based on application logic:

php
public function updateProfile()
{
    $data = signals()->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|max:255|unique:email',
    ]);
    $user = auth()->user();
    $user->update($data);

    hyper()->signals(['message' => 'Profile updated!']);

    if ($user->wasChanged('email')) {
        hyper()->signals([
            'emailVerified' => false,
            'notice' => 'Please verify your new email address'
        ]);
    }

    return hyper();
}

Building Responses Conditionally

php
public function toggle($id)
{
    $todo = Todo::findOrFail($id);
    $todo->update(['completed' => !$todo->completed]);

    hyper()->signals([
        "todos.{$id}.completed" => $todo->completed
    ]);

    if ($todo->completed) {
        hyper->signals(['completedCount' => Todo::where('completed', true)->count()]);
    }

    return hyper();
}

Fluent Conditional Methods

Hyper provides Laravel-style fluent conditional methods for building dynamic responses.

when()

Execute a callback when a condition is true:

php
public function index()
{
    $posts = Post::latest()->paginate(10);

    return hyper()
        ->fragment('posts.index', 'post-list', compact('posts'))
        ->when(auth()->user()->isAdmin(), function ($hyper) {
            hyper()->signals(['isAdmin' => true]);
        });
}

The callback receives the HyperResponse instance, allowing you to chain additional methods.

With Fallback

Provide a callback for when the condition is false:

php
return hyper()
    ->fragment('dashboard', 'content', $data)
    ->when(
        auth()->user()->hasVerifiedEmail(),
        fn($hyper) => $hyper->signals(['verified' => true]),
        fn($hyper) => $hyper->signals(['showVerificationNotice' => true])
    );

unless()

Execute a callback when a condition is false:

php
return hyper()
    ->fragment('todos', 'list', $data)
    ->unless(auth()->user()->hasPremium(), function ($hyper) {
        hyper()->signals(['showUpgradePrompt' => true]);
    });

whenHyper()

Execute a callback only for Hyper requests:

php
return hyper()
    ->view('dashboard', $data)
    ->whenHyper(function ($hyper) {
        // Additional operations only for Hyper requests
        hyper()->js('console("Dashboard updated via Hyper")');
    });

With Fallback

php
return hyper()
    ->whenHyper(
        fn($r) => $r->fragment('products', 'list', $data),
        fn($r) => $r->view('products.index', $data)
    );

whenNotHyper()

Execute a callback only for traditional requests:

php
return hyper()
    ->whenNotHyper(function ($response) {
        // Set full page layout data
        $response->view('dashboard', array_merge($data, [
            'layout' => 'app',
            'title' => 'Dashboard'
        ]));
    });

Detecting Navigation Requests

Check if a request is from client-side navigation:

php
public function products()
{
    if (request()->isHyperNavigate()) {
        // Client-side navigation - partial update
        return hyper()->fragment('products.index', 'main-content', $data);
    }

    // Initial page load - full view
    return view('products.index', $data);
}

Check for specific navigation keys to provide targeted updates:

php
public function dashboard()
{
    if (request()->isHyperNavigate('sidebar')) {
        // Only update sidebar
        return hyper()->fragment('dashboard', 'sidebar', $sidebarData);
    }

    if (request()->isHyperNavigate('content')) {
        // Only update main content
        return hyper()->fragment('dashboard', 'main-content', $contentData);
    }

    // Full page load
    return view('dashboard', array_merge($sidebarData, $contentData));
}

whenHyperNavigate()

Use the fluent method for navigation-based conditions:

php
return hyper()
    ->fragment('products.index', 'product-list', $data)
    ->whenHyperNavigate('filters', function ($response) {
        $response->fragment('products.index', 'filter-sidebar', $filterData);
    });

Check for Specific Keys

php
return hyper()
    ->whenHyperNavigate('full',
        fn($r) => $r->fragment('layout', 'full-page', $data)
    )
    ->whenHyperNavigate('partial',
        fn($r) => $r->fragment('layout', 'content-only', $data)
    );

Permission-Based Responses

Role-Based Content

php
public function dashboard()
{
    $data = $this->getDashboardData();

    return hyper()
        ->fragment('dashboard', 'main', $data)
        ->when(auth()->user()->isAdmin(), function ($response) use ($data) {
            $response->fragment('dashboard', 'admin-panel', $data);
        })
        ->when(auth()->user()->canManageUsers(), function ($response) {
            $response->signals(['showUserManagement' => true]);
        });
}

Ability-Based Logic

php
public function updatePost($id)
{
    $post = Post::findOrFail($id);

    if (auth()->user()->cannot('update', $post)) {
        return hyper()->signals([
            'error' => 'You do not have permission to edit this post.'
        ]);
    }

    $post->update(signals()->only(['title', 'content']));

    return hyper()
        ->fragment('posts.show', 'post-content', compact('post'))
        ->signals(['message' => 'Post updated successfully!']);
}

State-Based Responses

Based on Model State

php
public function publish($id)
{
    $post = Post::findOrFail($id);
    $post->update(['published_at' => now()]);

    return hyper()
        ->fragment('posts.index', 'post-list', ['posts' => Post::latest()->get()])
        ->when($post->wasChanged('published_at'), function ($response) use ($post) {
            $response->signals([
                'message' => 'Post published!',
                'publishedCount' => Post::whereNotNull('published_at')->count()
            ]);
        });
}

Based on Request Data

php
public function filter()
{
    $category = signals('category');
    $priceRange = signals('priceRange');

    $query = Product::query();

    if ($category) {
        $query->where('category_id', $category);
    }

    if ($priceRange) {
        [$min, $max] = explode('-', $priceRange);
        $query->whereBetween('price', [$min, $max]);
    }

    $products = $query->paginate(20);

    return hyper()
        ->fragment('products.index', 'product-grid', compact('products'))
        ->when($products->isEmpty(), function ($response) {
            $response->signals([
                'notice' => 'No products match your filters.'
            ]);
        });
}

Error Handling

Validation Failures

php
public function store()
{
    try {
        $validated = signals()->validate([
            'name' => 'required|max:255',
            'email' => 'required|email|unique:users'
        ]);

        $user = User::create($validated);

        return hyper()
            ->fragment('users.index', 'user-list', ['users' => User::latest()->get()])
            ->signals(['errors' => [], 'userForm' => []]);

    } catch (\Dancycodes\Hyper\Exceptions\HyperValidationException $e) {
        // Errors automatically set to signals, just return
        return hyper();
    }
}

Exception-Based Responses

php
public function update($id)
{
    try {
        $contact = Contact::findOrFail($id);
        $contact->update(signals()->only(['name', 'email', 'phone']));

        return hyper()
            ->fragment('contacts.show', 'contact-details', compact('contact'))
            ->signals(['message' => 'Contact updated!', 'errors' => []]);

    } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
        return hyper()->signals([
            'error' => 'Contact not found.'
        ]);

    } catch (\Exception $e) {
        return hyper()
            ->signals(['error' => 'An unexpected error occurred.'])
            ->js('console("Update failed: " . $e->getMessage(), "error")');
    }
}

Common Patterns

Progressive Enhancement

Serve full pages for initial loads, fragments for Hyper interactions:

php
public function search()
{
    $query = request('q');
    $results = Product::search($query)->paginate(20);

    if (request()->isHyper()) {
        return hyper()->fragment('search.results', 'results-list', compact('results', 'query'));
    }

    return view('search.results', compact('results', 'query'));
}

Admin-Specific Features

php
public function index()
{
    $posts = Post::latest()->paginate(10);

    return hyper()
        ->fragment('posts.index', 'post-list', compact('posts'))
        ->when(auth()->user()?->isAdmin(), function ($response) {
            $response
                ->signals(['canBulkEdit' => true])
                ->fragment('posts.index', 'admin-toolbar', [
                    'stats' => Post::selectRaw('count(*) as total, status')->groupBy('status')->get()
                ]);
        });
}

Learn More