Make QuickSwitcher search across all locales
Delegates to DocumentService::search which queries DocumentTranslation and collapses to distinct documents. Display titles use the Document title accessor (current locale + fallback).
This commit is contained in:
@@ -4,65 +4,32 @@
|
|||||||
|
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Services\DocumentService;
|
use App\Services\DocumentService;
|
||||||
use Livewire\Component;
|
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class QuickSwitcher extends Component
|
class QuickSwitcher extends Component
|
||||||
{
|
{
|
||||||
public $search = '';
|
public string $search = '';
|
||||||
public $selectedIndex = 0;
|
public int $selectedIndex = 0;
|
||||||
|
|
||||||
#[Computed]
|
#[Computed]
|
||||||
public function results()
|
public function results()
|
||||||
{
|
{
|
||||||
if (empty($this->search)) {
|
if (empty($this->search)) {
|
||||||
return Document::select('id', 'title', 'slug', 'path', 'updated_at')
|
$documents = Document::with('translations')
|
||||||
->orderBy('updated_at', 'desc')
|
->orderBy('updated_at', 'desc')
|
||||||
->limit(10)
|
->limit(10)
|
||||||
->get()
|
->get();
|
||||||
->map(fn($doc) => [
|
} else {
|
||||||
'id' => $doc->id,
|
$documents = app(DocumentService::class)->search($this->search, 10);
|
||||||
'title' => $doc->title,
|
|
||||||
'slug' => $doc->slug,
|
|
||||||
'directory' => dirname($doc->path),
|
|
||||||
])
|
|
||||||
->toArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FULLTEXT検索を使用(日本語対応)
|
return $documents->map(fn ($doc) => [
|
||||||
$results = Document::select('id', 'title', 'slug', 'path', 'updated_at')
|
'id' => $doc->id,
|
||||||
->whereRaw('MATCH(title, content) AGAINST(? IN BOOLEAN MODE)', [$this->search])
|
'title' => $doc->title,
|
||||||
->orderBy('updated_at', 'desc')
|
'slug' => $doc->slug,
|
||||||
->limit(10)
|
'directory' => dirname($doc->path),
|
||||||
->get()
|
])->values()->toArray();
|
||||||
->map(fn($doc) => [
|
|
||||||
'id' => $doc->id,
|
|
||||||
'title' => $doc->title,
|
|
||||||
'slug' => $doc->slug,
|
|
||||||
'directory' => dirname($doc->path),
|
|
||||||
])
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
// FULLTEXT検索で結果がない場合は LIKE 検索にフォールバック
|
|
||||||
if (empty($results)) {
|
|
||||||
$results = Document::select('id', 'title', 'slug', 'path', 'updated_at')
|
|
||||||
->where(function($query) {
|
|
||||||
$query->where('title', 'like', '%' . $this->search . '%')
|
|
||||||
->orWhere('content', 'like', '%' . $this->search . '%');
|
|
||||||
})
|
|
||||||
->orderBy('updated_at', 'desc')
|
|
||||||
->limit(10)
|
|
||||||
->get()
|
|
||||||
->map(fn($doc) => [
|
|
||||||
'id' => $doc->id,
|
|
||||||
'title' => $doc->title,
|
|
||||||
'slug' => $doc->slug,
|
|
||||||
'directory' => dirname($doc->path),
|
|
||||||
])
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updated($propertyName)
|
public function updated($propertyName)
|
||||||
@@ -92,8 +59,6 @@ public function selectDocument()
|
|||||||
$results = $this->results;
|
$results = $this->results;
|
||||||
if (isset($results[$this->selectedIndex])) {
|
if (isset($results[$this->selectedIndex])) {
|
||||||
$document = $results[$this->selectedIndex];
|
$document = $results[$this->selectedIndex];
|
||||||
|
|
||||||
// slug が存在することを確認
|
|
||||||
if (!empty($document['slug'])) {
|
if (!empty($document['slug'])) {
|
||||||
return $this->redirect(route('documents.show', $document['slug']));
|
return $this->redirect(route('documents.show', $document['slug']));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,4 +102,29 @@ public function test_editor_for_missing_locale_shows_empty_form_with_new_locale_
|
|||||||
// The blade should render a tab marked active for ja with empty inputs
|
// The blade should render a tab marked active for ja with empty inputs
|
||||||
$response->assertSeeText(__('messages.locale_names.ja', [], 'en'));
|
$response->assertSeeText(__('messages.locale_names.ja', [], 'en'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_quick_switcher_finds_documents_by_any_locale_title(): void
|
||||||
|
{
|
||||||
|
$doc = Document::factory()->create(['default_locale' => 'en', 'slug' => 'qs']);
|
||||||
|
$doc->translations()->where('locale', 'en')->update(['title' => 'Getting Started', 'content' => 'EN body']);
|
||||||
|
DocumentTranslation::factory()->create([
|
||||||
|
'document_id' => $doc->id,
|
||||||
|
'locale' => 'ja',
|
||||||
|
'title' => 'はじめに',
|
||||||
|
'content' => '本文',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$component = \Livewire\Livewire::test(\App\Livewire\QuickSwitcher::class)
|
||||||
|
->set('search', 'はじめに');
|
||||||
|
|
||||||
|
$results = $component->get('results');
|
||||||
|
$this->assertCount(1, $results);
|
||||||
|
$this->assertSame($doc->id, $results[0]['id']);
|
||||||
|
|
||||||
|
$component2 = \Livewire\Livewire::test(\App\Livewire\QuickSwitcher::class)
|
||||||
|
->set('search', 'Getting');
|
||||||
|
$results2 = $component2->get('results');
|
||||||
|
$this->assertCount(1, $results2);
|
||||||
|
$this->assertSame($doc->id, $results2[0]['id']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user