# Story 5.5: Post Search ## Epic Reference **Epic 5:** Posts/Blog System ## User Story As a **website visitor**, I want **to search through blog posts**, So that **I can find relevant legal articles and information**. ## Story Context ### Existing System Integration - **Integrates with:** posts table, public posts listing - **Technology:** Livewire Volt with real-time search - **Follows pattern:** Search component pattern - **Touch points:** Posts listing page ## Acceptance Criteria ### Search Functionality - [ ] Search input on posts listing page - [ ] Search by title (both languages) - [ ] Search by body content (both languages) - [ ] Real-time search results (debounced) ### User Experience - [ ] "No results found" message when empty - [ ] Clear search button - [ ] Search works in both Arabic and English - [ ] Only searches published posts ### Optional Enhancements - [ ] Search highlights in results - [ ] Search suggestions ### Quality Requirements - [ ] Debounce to reduce server load (300ms) - [ ] Case-insensitive search - [ ] Tests for search functionality ## Technical Notes ### Updated Posts Index Component ```php resetPage(); } public function clearSearch(): void { $this->search = ''; $this->resetPage(); } public function with(): array { return [ 'posts' => Post::published() ->when($this->search, function ($query) { $search = $this->search; $query->where(function ($q) use ($search) { $q->where('title_ar', 'like', "%{$search}%") ->orWhere('title_en', 'like', "%{$search}%") ->orWhere('body_ar', 'like', "%{$search}%") ->orWhere('body_en', 'like', "%{$search}%"); }); }) ->latest() ->paginate(10), ]; } }; ?>
{{ __('posts.title') }}
@if($search) @endif
@if($search)

@if($posts->total() > 0) {{ __('posts.search_results', ['count' => $posts->total(), 'query' => $search]) }} @else {{ __('posts.no_results', ['query' => $search]) }} @endif

@endif
@forelse($posts as $post) @empty
@if($search)

{{ __('posts.no_results', ['query' => $search]) }}

{{ __('posts.clear_search') }} @else

{{ __('posts.no_posts') }}

@endif
@endforelse
{{ $posts->links() }}
``` ### Highlight Helper Method ```php public function highlightSearch(string $text, string $search): string { if (empty($search)) { return e($text); } $escapedText = e($text); $escapedSearch = e($search); return preg_replace( '/(' . preg_quote($escapedSearch, '/') . ')/iu', '$1', $escapedText ); } ``` ### Translation Strings ```php // resources/lang/en/posts.php return [ 'search_placeholder' => 'Search articles...', 'search_results' => 'Found :count results for ":query"', 'no_results' => 'No results found for ":query"', 'clear_search' => 'Clear search', ]; // resources/lang/ar/posts.php return [ 'search_placeholder' => 'البحث في المقالات...', 'search_results' => 'تم العثور على :count نتيجة لـ ":query"', 'no_results' => 'لم يتم العثور على نتائج لـ ":query"', 'clear_search' => 'مسح البحث', ]; ``` ### Testing ```php it('searches posts by title', function () { Post::factory()->published()->create(['title_en' => 'Legal Rights Guide']); Post::factory()->published()->create(['title_en' => 'Tax Information']); Volt::test('posts.index') ->set('search', 'Legal') ->assertSee('Legal Rights Guide') ->assertDontSee('Tax Information'); }); it('searches posts by body content', function () { Post::factory()->published()->create([ 'title_en' => 'Post 1', 'body_en' => 'This discusses property law topics.', ]); Post::factory()->published()->create([ 'title_en' => 'Post 2', 'body_en' => 'This is about family matters.', ]); Volt::test('posts.index') ->set('search', 'property') ->assertSee('Post 1') ->assertDontSee('Post 2'); }); it('shows no results message', function () { Volt::test('posts.index') ->set('search', 'nonexistent') ->assertSee('No results found'); }); it('only searches published posts', function () { Post::factory()->create(['status' => 'draft', 'title_en' => 'Draft Post']); Post::factory()->published()->create(['title_en' => 'Published Post']); Volt::test('posts.index') ->set('search', 'Post') ->assertSee('Published Post') ->assertDontSee('Draft Post'); }); ``` ## Definition of Done - [ ] Search input on posts page - [ ] Searches title in both languages - [ ] Searches body in both languages - [ ] Real-time results with debounce - [ ] Clear search button works - [ ] "No results" message shows - [ ] Only published posts searched - [ ] Search highlighting works - [ ] Tests pass - [ ] Code formatted with Pint ## Dependencies - **Story 5.4:** Public posts display ## Estimation **Complexity:** Low-Medium **Estimated Effort:** 2-3 hours