Skip to main content
WireChat provides powerful search functionality for finding conversations and users quickly. Search can be customized and extended to match your application’s needs. Enable search in the conversation list:
use Wirechat\Wirechat\Panel;

Panel::make('admin')
    ->chatsSearch()  // Enable conversation search
    // ... other configuration

How It Works

The chat search queries both participant names and group information:
// src/Livewire/Chats/Chats.php:292-315
protected function applySearchConditions($query): Builder
{
    $searchableFields = $this->panel()->getSearchableAttributes();
    $groupSearchableFields = ['name', 'description'];
    
    return $query->withDeleted()->where(function ($query) use ($searchableFields, $groupSearchableFields) {
        // Search in participant names
        $query->whereHas('participants', function ($subquery) use ($searchableFields) {
            $subquery->whereHas('participantable', function ($query2) use ($searchableFields) {
                $query2->where(function ($query3) use ($searchableFields) {
                    foreach ($searchableFields as $field) {
                        $query3->orWhere($field, 'LIKE', '%'.$this->search.'%');
                    }
                });
            });
        });
        
        // Search in group names and descriptions
        return $query->orWhereHas('group', function ($groupQuery) use ($groupSearchableFields) {
            $groupQuery->where(function ($query4) use ($groupSearchableFields) {
                foreach ($groupSearchableFields as $field) {
                    $query4->orWhere($field, 'LIKE', '%'.$this->search.'%');
                }
            });
        });
    });
}

Searchable User Attributes

Define which user fields should be searchable:
Panel::make('admin')
    ->searchableAttributes(['name', 'email', 'username'])
By default, WireChat searches the name field:
// src/Panel/Concerns/HasSearchableAttributes.php
protected array $searchableAttributes = ['name'];

Multiple Fields

Search across multiple user attributes:
Panel::make('admin')
    ->searchableAttributes([
        'name',
        'email', 
        'username',
        'profile->bio',  // JSON column
    ])
Implement custom user search for starting new conversations:
Panel::make('admin')
    ->searchUsersUsing(function (string $search) {
        return User::query()
            ->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                  ->orWhere('email', 'like', "%{$search}%")
                  ->orWhere('username', 'like', "%{$search}%");
            })
            ->where('id', '!=', auth()->id())  // Exclude current user
            ->limit(20)
            ->get();
    })
The search callback must return a Collection of models, not a query builder.
If no custom search is provided, WireChat uses the default implementation:
// src/Panel/Concerns/HasUsersSearch.php:47-56
return \App\Models\User::query()
    ->where(function ($q) use ($needle) {
        foreach ($this->getSearchableAttributes() as $field) {
            $q->orWhere($field, 'like', "%{$needle}%");
        }
    })
    ->limit(20)
    ->get();

Search Results

User search results are automatically formatted using the WirechatUserResource:
// src/Panel/Concerns/HasUsersSearch.php:24-28
public function searchUsers(?string $needle)
{
    return WirechatUserResource::collection(
        $this->runSearchCallback($needle)
    );
}

Search in Conversations List

The conversation list automatically applies search when the user types:
// src/Livewire/Chats/Chats.php:188-192
public function updatedSearch($value)
{
    $this->conversations = [];  // Clear previous results
    $this->reset(['page', 'canLoadMore']);
}

Search Behavior

1

Empty Search

When search is empty, conversations are filtered to exclude blanks and deleted chats:
->when(trim($this->search ?? '') == '', function ($query) {
    return $query->withoutDeleted()->withoutBlanks();
})
2

Active Search

When searching, deleted conversations are included in results:
->when(trim($this->search ?? '') != '', fn ($query) => 
    $this->applySearchConditions($query)
)
Implement complex search logic:
Panel::make('admin')
    ->searchUsersUsing(function (string $search) {
        $currentUser = auth()->user();
        
        return User::query()
            // Search by name, email, username
            ->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                  ->orWhere('email', 'like', "%{$search}%")
                  ->orWhere('username', 'like', "%{$search}%");
            })
            // Exclude current user
            ->where('id', '!=', $currentUser->id)
            // Only active users
            ->where('status', 'active')
            // Order by relevance
            ->orderByRaw(
                "CASE 
                    WHEN name LIKE ? THEN 1
                    WHEN username LIKE ? THEN 2
                    WHEN email LIKE ? THEN 3
                    ELSE 4
                END",
                ["{$search}%", "{$search}%", "{$search}%"]
            )
            ->limit(20)
            ->get();
    })

Searching with Relationships

Include related data in search:
Panel::make('admin')
    ->searchUsersUsing(function (string $search) {
        return User::with(['profile', 'department'])
            ->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                  // Search in relationships
                  ->orWhereHas('department', function ($dq) use ($search) {
                      $dq->where('name', 'like', "%{$search}%");
                  })
                  ->orWhereHas('profile', function ($pq) use ($search) {
                      $pq->where('bio', 'like', "%{$search}%");
                  });
            })
            ->limit(20)
            ->get();
    })

Column Existence Check

WireChat safely checks for column existence before searching:
// src/Livewire/Chats/Chats.php:326-332
protected function columnExists($table, $field, &$columnCache)
{
    if (!isset($columnCache[$table])) {
        $columnCache[$table] = Schema::getColumnListing($table);
    }
    
    return in_array($field, $columnCache[$table]);
}
Column existence is cached per request to improve performance when searching across multiple fields.

Search Performance

Optimize search for better performance:

Add Database Indexes

// In a migration
Schema::table('users', function (Blueprint $table) {
    $table->index('name');
    $table->index('email');
    $table->index('username');
    
    // Full-text search (MySQL 5.7+)
    $table->fullText(['name', 'email', 'username']);
});
Panel::make('admin')
    ->searchUsersUsing(function (string $search) {
        return User::query()
            ->whereFullText(['name', 'email', 'username'], $search)
            ->limit(20)
            ->get();
    })

Implement Search Service

For large-scale applications, use dedicated search services:
use Laravel\Scout\Searchable;

class User extends Model
{
    use Searchable;
    
    public function toSearchableArray()
    {
        return [
            'name' => $this->name,
            'email' => $this->email,
            'username' => $this->username,
        ];
    }
}

// In panel configuration
Panel::make('admin')
    ->searchUsersUsing(function (string $search) {
        return User::search($search)
            ->take(20)
            ->get();
    })
To disable search functionality:
Panel::make('admin')
    ->chatsSearch(false)  // Disable conversation search

Search UI Behavior

Real-time Search

Search results update as users type in the search field

Deleted Conversations

Deleted chats appear in search results for easy recovery

Pagination Reset

Search automatically resets pagination to show fresh results

Empty State

Clear feedback when no results match the search query
Test your search implementation:
use App\Models\User;

/** @test */
public function it_searches_users_by_name()
{
    $user1 = User::factory()->create(['name' => 'John Doe']);
    $user2 = User::factory()->create(['name' => 'Jane Smith']);
    $user3 = User::factory()->create(['name' => 'Bob Johnson']);
    
    $panel = Wirechat::panel('admin');
    $results = $panel->searchUsers('John');
    
    $this->assertCount(2, $results);
    $this->assertTrue($results->contains('id', $user1->id));
    $this->assertTrue($results->contains('id', $user3->id));
}

Best Practices

Limit Results

Always limit search results (e.g., 20 users) for performance

Add Indexes

Create database indexes on searchable fields

Case Sensitivity

Use case-insensitive search with LIKE or ILIKE

Exclude Current User

Remove the authenticated user from user search results

Next Steps

Private Chats

Start conversations from search results

Theming

Customize the search interface appearance

User Setup

Configure user model for search

Permissions

Control who can search and create conversations