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:
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:
@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>
@endifhyperThis 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:
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:
// 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:
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
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:
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:
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:
return hyper()
->fragment('todos', 'list', $data)
->unless(auth()->user()->hasPremium(), function ($hyper) {
hyper()->signals(['showUpgradePrompt' => true]);
});whenHyper()
Execute a callback only for Hyper requests:
return hyper()
->view('dashboard', $data)
->whenHyper(function ($hyper) {
// Additional operations only for Hyper requests
hyper()->js('console("Dashboard updated via Hyper")');
});With Fallback
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:
return hyper()
->whenNotHyper(function ($response) {
// Set full page layout data
$response->view('dashboard', array_merge($data, [
'layout' => 'app',
'title' => 'Dashboard'
]));
});Navigation-Based Conditions
Detecting Navigation Requests
Check if a request is from client-side navigation:
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);
}Navigation Keys
Check for specific navigation keys to provide targeted updates:
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:
return hyper()
->fragment('products.index', 'product-list', $data)
->whenHyperNavigate('filters', function ($response) {
$response->fragment('products.index', 'filter-sidebar', $filterData);
});Check for Specific Keys
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
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
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
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
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
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
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:
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
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
- Response Builder - Core response building methods
- Dual Responses - Progressive enhancement patterns
- Request Macros - Available request helper methods
- Validation - Handling validation in Hyper requests

